/*!
*
* \file    MxdCmmbDemuxer.cpp
*
* \brief    Demuxer Interface cpp file.
*
* Header file of the functions declaration about MIRA CMMB Demuxer
*
* \par    Include files
* - MxdCmmbDemuxer.h
*
* \par    Copyright (c) 2008 Maxscend Technologies Inc. All rights reserved
*
* PROPRIETARY RIGHTS of Maxscend Technologies Inc. are involved in
* the subject matter of this material.  All manufacturing, reproduction,
* use, and sales rights pertaining to this subject matter are governed
* by the license agreement.  The recipient of this software implicitly
* accepts the terms of the license.
*
* \version
* Revision of last commit: $Rev: 1003 $
* Author of last commit  : $Author: maxscend\chaohui.jiang $
* Date of last commit    : $Date: 2011-01-05 16:31:57 +0800 (星期三, 05 一月 2011) $
*
*/

#include "Mxd0250Private.h"
#include "Mxd0250FrameProcess.h"

#include "Mxd0250Frame.h"

#ifndef USE_EXTERNAL_DEMUXER
#ifdef ESG_FILES_IN_CURRENT_PATH
#include "gunzip.h"
#include "nxml.h"
#else
#include "./OptionalCode/gunzip.h"
#include "./OptionalCode/nxml.h"
#endif //APPEND_ACC_HEADER_IN_DEMUXER
#endif //USE_EXTERNAL_DEMUXER



void PrintCmmbUnit( CMMBUNIT_DEFINE* pUnit );

/*Private functions*/
void sEmgSaver_Init( EMGSAVER *pThis );
void sEmgSaver_Destory( EMGSAVER *pThis );
MXD_BOOL sEmgSaver_IsComplete( EMGSAVER *pThis );
MXD_UINT8* sEmgSaver_GetTotalBuffer( EMGSAVER *pThis );

void sEmgStg_Init( EMGSTG* pThis );
void sEmgStg_Destory( EMGSTG* pThis );
EMGSAVER* sEmgStg_GetMsgData( EMGSTG* pThis, MXD_UINT32 nMsgId );
MXD_BOOL sEmgStg_AddSection( EMGSTG* pThis, MXD_UINT32 nMsgId, MXD_UINT8* pMsgData, MXD_UINT32 nSectionLen, MXD_UINT8 nCurrentSection, MXD_UINT8 nTotalSection );
MXD_BOOL sEmgStg_FindCompleteMsgData( EMGSTG* pThis, struct sEmergencyMsgDataSaver** ppEmgMsgData );
MXD_BOOL sEmgStg_IsBrokenMsgDataExist( EMGSTG* pThis );

MXD_RETURN_CODE DMX_PVT_SendEmergencyMsg( MXD_UINT32 nMsgId, CMMBUNIT_DEFINE* pEmergencyMsg, MXD_UINT8 nDemodChipIndex, CB_OnEmergencyMsg OnEmergencyMsg );

#ifndef USE_EXTERNAL_DEMUXER
void RemoveAllAuxInfo( MXD_ESG_DATA *pEsgData );
void RemoveAllContentInfo( MXD_ESG_DATA *pEsgData );
void RemoveAllParaInfo( MXD_ESG_DATA *pEsgData );
void RemoveAllSchInfo( MXD_ESG_DATA *pEsgData );
void RemoveAllSrvInfo( MXD_ESG_DATA *pEsgData );
MXD_UINT32 CreateOrExpandInfoArray( MXD_UINT16 *pOrgCount, void** ppOrgArray, MXD_UINT32 nUnitSize, MXD_UINT16 nCount );
MXD_BOOL BuildServiceInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* root );
MXD_BOOL BuildSchInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* root );
MXD_BOOL BuildParaInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* root );
MXD_BOOL BuildContentInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* root );
MXD_BOOL BuildAuxInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* root );

void sEsgSaver_Init( ESGSAVER* pThis );
void sEsgSaver_Destory( ESGSAVER* pThis );
MXD_BOOL sEsgSaver_IsComplete( ESGSAVER* pThis );
MXD_UINT8* sEsgSaver_GetTotalBuffer( ESGSAVER* pThis, MXD_UINT32* pTotalLen );

void sEsgStg_Init( ESGSTG* pThis );
void sEsgStg_Destory( ESGSTG* pThis );
ESGSAVER* sEsgStg_GetSavedBlock( ESGSTG* pThis, MXD_UINT8 nBlockId, MXD_UINT8 nBlockTypeId );
MXD_BOOL sEsgStg_AddBlock( ESGSTG* pThis, MXD_UINT8 nBlockId, MXD_UINT8 nBlockTypeId, MXD_UINT8* pMsgData, MXD_UINT32 nBlockLen, MXD_UINT8 nCurrentBlock, MXD_UINT8 nTotalBlock, MXD_BOOL bZipBlock );
MXD_BOOL sEsgStg_FindCompleteEsgBlock( ESGSTG* pThis, ESGSAVER** ppEsgBlockSaver );

MXD_RETURN_CODE DMX_PVT_SendVideoFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT8 nDemodChipIndex, MXD_UINT32 nServiceID, void* pSeviceUserParam, MXD_DEMUXER_CONTEXT* pContext, CB_OnVideoUnit OnVideoUnit );
MXD_RETURN_CODE DMX_PVT_SendAudioFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT8 nDemodChipIndex, MXD_UINT32 nServiceID, void* pSeviceUserParam, MXD_DEMUXER_CONTEXT* pContext, CB_OnAudioUnit OnAudioUnit );
MXD_RETURN_CODE DMX_PVT_SendDataFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT32 nServiceID, void* pSeviceUserParam, MIRA_DEVICE_HANDLE* pMiraDevice );
MXD_RETURN_CODE DMX_PVT_FillMediaUnitFromFrame( CMMBUNIT_DEFINE* pFrame, MXD_MEDIA_UNIT_IN_FRAME* pMediaUnits );
MXD_RETURN_CODE DMX_PVT_SendEcmData( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT32 nServiceID, void* pSeviceUserParam, MIRA_DEVICE_HANDLE* pMiraDevice );

MXD_UINT32 DMX_PVT_CreateRtpVideoUnit( MXD_BOOL bIsRawH264Video, CMMBUNIT_DEFINE* pInnoVideoFragment, MXD_UINT32 nStartPlayTime, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nPacketLen );
MXD_UINT32 DMX_PVT_CreateRtpAudioUnit( MXD_BOOL bIsRawHAacAudio, MXD_UINT8 nPacketType, MXD_UINT8 nEndIdc, CMMBUNIT_DEFINE* pRawAacPacket, MXD_UINT32 nStartPlayTime, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nAudioLen );

MXD_UINT32 DMX_PVT_SendOutRtpVideoUnit( MXD_UINT8* pRawVideoContent, MXD_STREAM_VIDEO_FRAME vFrame, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nPacketLen, CB_OnVideoUnit OnVideoUnit, MXD_UINT8 nDemodChipIndex, MXD_UINT32 serviceID, void* userParam, MXD_VIDEO_PARAM *tParam );

void Context_CheckMediaBuffer( struct sDemuxerContext* pThis, MXD_UINT32 nLen );
void Context_FillAacFrame( struct sDemuxerContext* pThis, MXD_UINT8* pSrcFrame, MXD_UINT32 nFrameLen, MXD_UINT32 nBufferBegin );
#endif //USE_EXTERNAL_DEMUXER

void Context_Init( struct sDemuxerContext* pThis );
void Context_Destory( struct sDemuxerContext* pThis );
MXD_BOOL Context_GetSrvIdAndUserParam( struct sDemuxerContext* pThis, MXD_UINT8 nMF_ID, MXD_UINT32* pServiceID, void** ppSeviceUserParam );
MXD_BOOL Context_GetMFID( struct sDemuxerContext* pThis, MXD_UINT32 nServiceID, MXD_UINT8 *pMF_ID, MXD_UINT8 *pSubFrameId );
MXD_BOOL Context_GetUserParam( struct sDemuxerContext* pThis, MXD_UINT32 nServiceID, void* *ppSeviceUserParam );

/*Private functions end*/

/*******************************************************************************
* \par Description
* This function is used to Register the Callbacks to demuxer, and demuxer will create private struct in the handle. 
*
* \param ;
*
* \return   
*    MXD_RTN_OK	-	the call succeeded
*    Others	-	the demuxer initialization failed.
*
* \warning    
*/
MXD_RETURN_CODE DMX_Initialize( MXD_HANDLE hDevice )
{
	MXD_DEMUXER_CONTEXT* pDemuxerContext = (MXD_DEMUXER_CONTEXT*) MXD_Malloc( sizeof(MXD_DEMUXER_CONTEXT) );
	Context_Init( pDemuxerContext );

	((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct = pDemuxerContext;

	return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to release the resource allocated by demuxer. 
*
* \param 
*
* \return   
*    MXD_RTN_OK	-	the call succeeded
*    Others	-	the demuxer destory failed.
*
* \warning    
*/
MXD_RETURN_CODE DMX_Destory( MXD_HANDLE hDevice )
{
	if( ((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct )
	{
		Context_Destory( (MXD_DEMUXER_CONTEXT*) (((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct) );
	}
	return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to reset the demuxer, it must be called after frequency changed.
*
* \param 
*
* \return   
*    MXD_RTN_OK	-	the call succeeded
*    Others	-	the Device reset failed.
*
* \warning    
*/
MXD_RETURN_CODE DMX_Reset( MXD_HANDLE hDevice, MXD_CMMBSERVICE_INFO *pSavedSeviceChannelList, MXD_UINT32 service_count )
{
	MXD_DEMUXER_CONTEXT* pContext = ((MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct);
	pContext->m_nServiceCount = 0;
	if( pContext->m_pSeviceChannelList != 0 )
	{
		MXD_Free( pContext->m_pSeviceChannelList );
		pContext->m_pSeviceChannelList = 0;
	}

	if( pSavedSeviceChannelList )
	{
		pContext->m_pSeviceChannelList = (MXD_CMMBSERVICE_INFO*) MXD_Malloc( sizeof(MXD_CMMBSERVICE_INFO)*service_count );
		MXD_Memcpy( pContext->m_pSeviceChannelList, pSavedSeviceChannelList, sizeof(MXD_CMMBSERVICE_INFO)*service_count );
		pContext->m_nServiceCount = service_count;
	}
	return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to check if the frame is valid CLCH frame. 
*
* \param data	[IN] frame data;
* \param len	[IN] frame length;
*
* \return   
*    MXD_RTN_OK	-  the frame is valid
*
* \warning    
*/
MXD_RETURN_CODE DMX_VerifyControlFrame( MXD_HANDLE hDevice, MXD_UINT8* data, MXD_INT len )
{
	MXD_INT32 nEatenBits;
	CMMBUNIT_DEFINE* pUnit;

	pUnit = FrameDecompose( (unsigned char*)data, len, &nEatenBits  );

	if( !pUnit || (pUnit+1/*ControlTables*/)->m_u1.m_pChildUnitList == 0 )
		return MXD_GENERAL_ERROR;
	else
		return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to pass in service list which saved by host.
*
* \param pinfo [IN] service info list;
* \param service_count	[IN] service count;
*
* \return   
*    MXD_RTN_OK	-  the service name is filled.
*    other - demuxer fail.
*
* \warning    
*/
MXD_RETURN_CODE DMX_SetServiceInfoList( MXD_HANDLE hDevice, MXD_CMMBSERVICE_INFO *pinfo, MXD_UINT32 nServiceCount )
{
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;

	if( pContext->m_pSeviceChannelList != 0 )
		MXD_Free( pContext->m_pSeviceChannelList );
	if( pContext->m_pUserParams != 0 )
		MXD_Free( pContext->m_pUserParams );

	pContext->m_pSeviceChannelList = (MXD_CMMBSERVICE_INFO*) MXD_Malloc( sizeof(MXD_CMMBSERVICE_INFO)*nServiceCount );
	MXD_Memcpy( pContext->m_pSeviceChannelList, pinfo, sizeof(MXD_CMMBSERVICE_INFO)*nServiceCount );
	pContext->m_nServiceCount = nServiceCount;
	pContext->m_pUserParams = (void**) MXD_Malloc( sizeof(void*)*nServiceCount );
	MXD_Memset( pContext->m_pUserParams, 0, sizeof(void*)*nServiceCount );

	return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to feed the CLCH frame data and get out the service list.
* Notice: the service name information must demux from esg data, this function will return an empty name.
*
* \param data	[IN] CLCH frame data;
* \param len	[IN] CLCH frame length;
* \param ppinfo [IN] service info list, will be malloced by demuxer;
*
* \return   
*    MXD_RTN_OK	-  the service name is filled.
*    MXD_DEMUXER_CONTINUE - demuxer have not get enough data from current ESG data, caller must feed more data.
*    other - demuxer fail.
*
* \warning    
*/
MXD_RETURN_CODE DMX_GetServiceInfoList( MXD_HANDLE hDevice, MXD_UINT8* data, MXD_INT len, MXD_CMMBSERVICE_INFO **ppinfo, MXD_UINT32* pservice_count )
{
	MXD_BOOL bDemuxOk;
	MXD_INT32 nEatenBits;
	CMMBUNIT_DEFINE *pUnit, *pCtrlTable;
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;
	CB_OnRawFrame					OnRawFrame = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawFrame;
	CB_OnEadtInfo					OnEadtInfo = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnEadtInfo;
	CMMBUNIT_DEFINE *pCMCT, *pESG, *pCAS, *pNIT;
	MXD_UINT32 i,j,k=0;
	*ppinfo = 0;
	*pservice_count = 0;

	if( OnRawFrame )
	{
		if( OnRawFrame( ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, 0, 0, data, len ) == MXD_DEMUXER_STOP )
			return MXD_RTN_OK;
	}

	InitUnitListStorage();
	pUnit = FrameDecompose( (unsigned char*)data, len, &nEatenBits  );

	if( !pUnit )
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	pCtrlTable = (pUnit+1/*ControlTables*/)->m_u1.m_pChildUnitList;
	if( !pUnit || pCtrlTable == 0 )
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	bDemuxOk = FALSE;
	while( pCtrlTable )
	{
		if( (pCMCT = FindUnitFromList( pCtrlTable, "CMCT" )->m_u1.m_pChildUnitList) != 0  )
		{
			CMMBUNIT_DEFINE *pServiceInfo, *pSubFrameInfo = NULL;
			MXD_UINT8 nServiceCount = 0;
			bDemuxOk = TRUE;
			if( pContext->m_pSeviceChannelList != 0 )
				MXD_Free( pContext->m_pSeviceChannelList );
			if( pContext->m_pUserParams != 0 )
				MXD_Free( pContext->m_pUserParams );

			pServiceInfo = FindUnitFromList( pCMCT, "ServiceInfos" )->m_u1.m_pChildUnitList;
			do
			{
				if( FindUnitFromList( pServiceInfo, "MF_ID" )->m_nValue == 0 )
				{
					pServiceInfo = GetNextSiblingInList( pServiceInfo );
					continue;
				}
				nServiceCount += FindUnitFromList( pServiceInfo, "SubFrameCount" )->m_nValue;
				pServiceInfo = GetNextSiblingInList( pServiceInfo );
			}while( pServiceInfo );
			pContext->m_pSeviceChannelList = (MXD_CMMBSERVICE_INFO*) MXD_Malloc( sizeof(MXD_CMMBSERVICE_INFO)*nServiceCount );
			MXD_Memset( pContext->m_pSeviceChannelList, 0, sizeof(MXD_CMMBSERVICE_INFO)*nServiceCount );
			pContext->m_nServiceCount = nServiceCount;
			pContext->m_pUserParams = (void**) MXD_Malloc( sizeof(void*)*nServiceCount );
			MXD_Memset( pContext->m_pUserParams, 0, sizeof(void*)*nServiceCount );

			for( i=0; i<FindUnitFromList( pCMCT, "ServiceInfoCount" )->m_nValue; i++ )
			{
				if( i==0 )
					pServiceInfo = FindUnitFromList( pCMCT, "ServiceInfos" )->m_u1.m_pChildUnitList;
				else
					pServiceInfo = GetNextSiblingInList( pServiceInfo );
				if( FindUnitFromList( pServiceInfo, "MF_ID" )->m_nValue == 0 )
					continue;
				for( j=0; j<FindUnitFromList( pServiceInfo, "SubFrameCount" )->m_nValue; j++ )
				{
					if( j==0 )
						pSubFrameInfo = FindUnitFromList( pServiceInfo, "SubFramesInfo" )->m_u1.m_pChildUnitList;
					else
						pSubFrameInfo = GetNextSiblingInList( pSubFrameInfo );
					pContext->m_pSeviceChannelList[k].nMF_ID = FindUnitFromList( pServiceInfo, "MF_ID" )->m_nValue;
					pContext->m_pSeviceChannelList[k].nServiceID = FindUnitFromList( pSubFrameInfo, "ServiceID" )->m_nValue;
					pContext->m_pSeviceChannelList[k].nSubFrameID = FindUnitFromList( pSubFrameInfo, "SubFrameNo" )->m_nValue;
					pContext->m_pSeviceChannelList[k].eLdpcMode = (MXD_LDPC_MODE) FindUnitFromList( pServiceInfo, "LDPC_CodeRate" )->m_nValue;
					pContext->m_pSeviceChannelList[k].eModuType = (MXD_MODULATE_TYPE) FindUnitFromList( pServiceInfo, "ModulateMode" )->m_nValue;
					pContext->m_pSeviceChannelList[k].eInterleaveMode = (MXD_INTERLEAVE_MODE) FindUnitFromList( pServiceInfo, "InterleaveMode" )->m_nValue;
					pContext->m_pSeviceChannelList[k].eRsMode = (MXD_RS_MODE) FindUnitFromList( pServiceInfo, "RS_CodeRate" )->m_nValue;
					pContext->m_pSeviceChannelList[k].nDisturbMode = (MXD_RS_MODE) FindUnitFromList( pServiceInfo, "DisturbMode" )->m_nValue;

					pContext->m_pSeviceChannelList[k].nTsCount = FindUnitFromList( pServiceInfo, "TimeSlotCount" )->m_nValue;
					//we assume the first time slot numer is the start ts.
					pContext->m_pSeviceChannelList[k].nStartTs = FindUnitFromList( FindUnitFromList( pServiceInfo, "TimeSlots" )->m_u1.m_pChildUnitList, "TimeSlotNo" )->m_nValue;
					k++;
				}
			}

			*ppinfo = pContext->m_pSeviceChannelList;
			*pservice_count = nServiceCount;
		}
		else if( (pESG = FindUnitFromList( pCtrlTable, "ESGTab" )->m_u1.m_pChildUnitList) != 0 )
		{
			if( FindUnitFromList( pESG, "ESGSeviceCount" )->m_nValue != 0 )
			{
				CMMBUNIT_DEFINE* pESGSeviceInfo = FindUnitFromList( pESG, "ESGSeviceInfos" )->m_u1.m_pChildUnitList;
				do
				{
					for( i=0; i<pContext->m_nServiceCount; i++ )
					{
						if( FindUnitFromList( pESGSeviceInfo, "ESGSeviceID" )->m_nValue == pContext->m_pSeviceChannelList[i].nServiceID )
							pContext->m_pSeviceChannelList[i].eServiceType = MXD_CMMBSERVICE_TYPE_ESG;
					}
					pESGSeviceInfo = GetNextSiblingInList( pESGSeviceInfo );
				}while( pESGSeviceInfo );
			}
			if( FindUnitFromList( pESG, "ESGDataTypeCount" )->m_nValue != 0 )
			{
				CMMBUNIT_DEFINE* pESGDataType = FindUnitFromList( pESG, "ESGDataTypes" )->m_u1.m_pChildUnitList;
				if( pContext->m_pEsgDataTypeIDs )
					MXD_Free( pContext->m_pEsgDataTypeIDs );
				pContext->m_nEsgDataTypeIDCount = FindUnitFromList( pESG, "ESGDataTypeCount" )->m_nValue;
				pContext->m_pEsgDataTypeIDs = MXD_Malloc( sizeof(MXD_UINT8) * pContext->m_nEsgDataTypeIDCount );
				i = 0;
				do
				{
					pContext->m_pEsgDataTypeIDs[i] = FindUnitFromList( pESGDataType, "ESGSeviceID" )->m_nValue;
					pESGDataType = GetNextSiblingInList( pESGDataType );
				}while( pESGDataType );
			}
		}
		else if( (pCAS = FindUnitFromList( pCtrlTable, "CASTab" )->m_u1.m_pChildUnitList) != 0 )
		{
			if( FindUnitFromList( pCAS, "SectionQuantity" )->m_nValue != 0 )
			{
				CMMBUNIT_DEFINE* pCASInfo, *pCATAB;
				MXD_UINT8 idxEadt=0;

				pContext->m_nEadtInfoCount = FindUnitFromList( pCAS, "SectionQuantity" )->m_nValue;
				if( pContext->m_pEadtInfoList != 0 )
					MXD_Free( pContext->m_pEadtInfoList );
				pContext->m_pEadtInfoList = (MXD_EADT_INFO*) MXD_Malloc( sizeof(MXD_EADT_INFO)*pContext->m_nEadtInfoCount );
				MXD_Memset( pContext->m_pEadtInfoList, 0, sizeof(MXD_EADT_INFO)*pContext->m_nEadtInfoCount );

				pCASInfo = FindUnitFromList( pCAS, "CAS_SectionInfos" )->m_u1.m_pChildUnitList;
				do
				{
					for( i=0; i<pContext->m_nServiceCount; i++ )
					{
						if( FindUnitFromList( pCASInfo, "Service_ID" )->m_nValue == pContext->m_pSeviceChannelList[i].nServiceID )
							pContext->m_pSeviceChannelList[i].eServiceType = MXD_CMMBSERVICE_TYPE_EMM;
					}

					pContext->m_pEadtInfoList[ idxEadt ].nCaId = FindUnitFromList( pCASInfo, "CA_System_ID" )->m_nValue;
					pContext->m_pEadtInfoList[ idxEadt ].nServiceId = FindUnitFromList( pCASInfo, "Service_ID" )->m_nValue;
					pContext->m_pEadtInfoList[ idxEadt ].nEcmDataType = FindUnitFromList( pCASInfo, "ECM_Data_Type" )->m_nValue;
					pContext->m_pEadtInfoList[ idxEadt ].nEmmDataType = FindUnitFromList( pCASInfo, "EMM_Data_Type" )->m_nValue;

					idxEadt++;
					pCASInfo = GetNextSiblingInList( pCASInfo );
				}while( pCASInfo );
				pCATAB = FindUnitFromList( pCtrlTable, "CASTab" );
				if( OnEadtInfo )
					OnEadtInfo( ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, 0, pContext->m_pEadtInfoList, pContext->m_nEadtInfoCount, pCATAB->m_pContent, pCATAB->m_nContentBitLen/8 );
			}
		}
		else if( (pNIT = FindUnitFromList( pCtrlTable, "NIT" )->m_u1.m_pChildUnitList) != 0 )
		{
			CMMBUNIT_DEFINE* pOtherFreq = NULL;
			CMMBUNIT_DEFINE* pOtherNet = NULL;
			pContext->m_sNitInfo.UpdateId = FindUnitFromList( pNIT, "NIT_No" )->m_nValue;
			if( FindUnitFromList( pNIT, "SystemTime" )->m_u1.m_pChildUnitList )
			{
				pContext->m_sNitInfo.SysTime.MJD_Date = FindUnitFromList( FindUnitFromList( pNIT, "SystemTime" )->m_u1.m_pChildUnitList, "MJD_Date" )->m_nValue;
				pContext->m_sNitInfo.SysTime.Hour = FindUnitFromList( FindUnitFromList( pNIT, "SystemTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 16 & 0xff;
				pContext->m_sNitInfo.SysTime.Minute = FindUnitFromList( FindUnitFromList( pNIT, "SystemTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 8 & 0xff;
				pContext->m_sNitInfo.SysTime.Second = FindUnitFromList( FindUnitFromList( pNIT, "SystemTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue & 0xff;
			}
			pContext->m_sNitInfo.CountryCode[0] = FindUnitFromList( pNIT, "Country" )->m_nValue>>16 & 0xff;
			pContext->m_sNitInfo.CountryCode[1] = FindUnitFromList( pNIT, "Country" )->m_nValue>>8 & 0xff;
			pContext->m_sNitInfo.CountryCode[2] = FindUnitFromList( pNIT, "Country" )->m_nValue & 0xff;
			pContext->m_sNitInfo.NetLevel = FindUnitFromList( pNIT, "NetLevel" )->m_nValue;
			pContext->m_sNitInfo.NetId = FindUnitFromList( pNIT, "NetNo" )->m_nValue;
			pContext->m_sNitInfo.NetNameLength = FindUnitFromList( pNIT, "NetNameLength" )->m_nValue;
			MXD_Memset( pContext->m_sNitInfo.NetName, 0, FindUnitFromList( pNIT, "NetName" )->m_nContentBitLen/8 + 1 );
			MXD_Memcpy( pContext->m_sNitInfo.NetName, FindUnitFromList( pNIT, "NetName" )->m_pContent, FindUnitFromList( pNIT, "NetName" )->m_nContentBitLen/8 );
			pContext->m_sNitInfo.FreqPointId = FindUnitFromList( pNIT, "FreqNo" )->m_nValue;
			pContext->m_sNitInfo.CentralFreq = FindUnitFromList( pNIT, "Freq" )->m_nValue;
			pContext->m_sNitInfo.BandWidth = FindUnitFromList( pNIT, "BandWidth" )->m_nValue;
			pContext->m_sNitInfo.OtherFreqPointNum = FindUnitFromList( pNIT, "FreqCount" )->m_nValue;
			for( i=0; i<15; i++ )
			{
				if( i==0 )
					pOtherFreq = FindUnitFromList( pNIT, "OtherFreqInfo" )->m_u1.m_pChildUnitList;
				else
					pOtherFreq = GetNextSiblingInList( pOtherFreq );
				if( ! pOtherFreq )
					break;
				pContext->m_sNitInfo.OtherFreqPoint[i].FreqPointId = FindUnitFromList( pOtherFreq, "OtherFreqNo" )->m_nValue;
				pContext->m_sNitInfo.OtherFreqPoint[i].CentralFreq = FindUnitFromList( pOtherFreq, "OtherFreq" )->m_nValue;
				pContext->m_sNitInfo.OtherFreqPoint[i].BandWidth = FindUnitFromList( pOtherFreq, "OtherBandWidth" )->m_nValue;
			}
			pContext->m_sNitInfo.NearNetNum = FindUnitFromList( pNIT, "OtherNetCount" )->m_nValue;
			for( i=0; i<15; i++ )
			{
				if( i==0 )
					pOtherNet = FindUnitFromList( pNIT, "OtherNetInfo" )->m_u1.m_pChildUnitList;
				else
					pOtherNet = GetNextSiblingInList( pOtherNet );
				if( ! pOtherNet )
					break;
				pContext->m_sNitInfo.NearNet[i].NetLevel = FindUnitFromList( pOtherNet, "OtherNetLevel" )->m_nValue;
				pContext->m_sNitInfo.NearNet[i].NetId = FindUnitFromList( pOtherNet, "OtherNetNo" )->m_nValue;
				pContext->m_sNitInfo.NearNet[i].FreqPoint.FreqPointId = FindUnitFromList( pOtherNet, "OtherNetFreqNo" )->m_nValue;
				pContext->m_sNitInfo.NearNet[i].FreqPoint.CentralFreq = FindUnitFromList( pOtherNet, "OtherNetFreq" )->m_nValue;
				pContext->m_sNitInfo.NearNet[i].FreqPoint.BandWidth = FindUnitFromList( pOtherNet, "OtherNetBandWidth" )->m_nValue;
			}
		}

		pCtrlTable = GetNextSiblingInList( pCtrlTable );
	}
	ClearUnitListStorage();
	return bDemuxOk ? MXD_RTN_OK : MXD_DEMUXER_CONTINUE;
}



/*******************************************************************************
* \par Description
* This function is used to feed the ESG frame data and demux will fill the service name to the list.
* Notice: current ESG frame data may have not enough information for demuxer to find out the service names. 
*      caller must feed more data when this function return MXD_DEMUXER_CONTINUE.
*
* \param data	[IN] ESG frame data;
* \param len	[IN] ESG frame length;
* \param ppinfo [IN] service info list, will be malloced by demuxer;
*
* \return   
*    MXD_RTN_OK	-  the service name is filled.
*    MXD_DEMUXER_CONTINUE - demuxer have not get enough data from current ESG data, caller must feed more data.
*    MXD_DEMUXER_NOTFOUND - demuxer have received duplicated data but not found the information for service name.
*
* \warning    
*/
MXD_RETURN_CODE DMX_GetServiceNameFromEsg( MXD_HANDLE hDevice, MXD_UINT8* data, MXD_INT len, MXD_CMMBSERVICE_INFO **ppinfo, MXD_UINT32 *pservice_count )
{
#ifndef USE_EXTERNAL_DEMUXER
	nxml_t xml;
#endif
	MXD_INT32 nEatenBits;
	CMMBUNIT_DEFINE *pFrame, *pEsgData;
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;
	MXD_CMMBSERVICE_INFO *pinfo = NULL;
	MXD_UINT32 service_count  = 0;
	CB_OnRawFrame              OnRawFrame = NULL;
	pinfo = pContext->m_pSeviceChannelList;
	service_count  = pContext->m_nServiceCount;
	OnRawFrame = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawFrame;

	if( OnRawFrame )
	{
		if( OnRawFrame( ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, 0, 0, data, len ) == MXD_DEMUXER_STOP )
			return MXD_RTN_OK;
	}

#ifdef USE_EXTERNAL_DEMUXER
	//this function is not available when using external demuxer.
	return MXD_GENERAL_ERROR;
#else
	*ppinfo = pinfo;
	*pservice_count =  service_count;
	InitUnitListStorage();
	pFrame = FrameDecompose( (unsigned char*)data, len, &nEatenBits  );

	if( !pFrame || 
		FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList == 0 || 
		FindUnitFromList( FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList, "DataSectionCtn" )->m_u1.m_pChildUnitList == 0 ||
		FindUnitFromList( FindUnitFromList( FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSliceInfos" )->m_u1.m_pChildUnitList == 0 ) //check if esg data slice
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	pEsgData = FindUnitFromList( FindUnitFromList( FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSlices" )->m_u1.m_pChildUnitList;

	while( pEsgData )
	{
		CMMBUNIT_DEFINE* pEsgDataSection = FindUnitFromList( pEsgData, "EsgDataSection" )->m_u1.m_pChildUnitList;
		if( pEsgDataSection && FindUnitFromList( pEsgDataSection, "EsgDataTypeID" )->m_nValue == 1 )
		{
			if( FindUnitFromList( pEsgDataSection, "BlockCount" )->m_nValue == 1 )
			{
				MXD_INT8* bufUnzip, *pZipContent;
				MXD_INT32 lenUnzip, lenZipContent;
				MXD_BOOL bNeedUnzip;

				lenZipContent = (MXD_INT32) FindUnitFromList( pEsgDataSection, "SliceLength" )->m_nValue;
				pZipContent = (MXD_INT8*) FindUnitFromList( pEsgDataSection, "EsgDataSlice" )->m_pContent;
				bNeedUnzip = FindUnitFromList( pEsgDataSection, "ZipType" )->m_nValue;

				if( bNeedUnzip )
				{
					MXD_Memcpy( &lenUnzip, pZipContent + lenZipContent - 4, 4 );
					if( lenUnzip > lenZipContent )
						bufUnzip = (MXD_INT8*) MXD_Malloc( lenUnzip );
					else
						bufUnzip = (MXD_INT8*) MXD_Malloc( lenZipContent*30 );

					lenUnzip = decompress_buf( pZipContent, lenZipContent, bufUnzip, 0 );
					if( lenUnzip == 0 )
					{
						MXD_Free( bufUnzip );
						ClearUnitListStorage();
						return MXD_GENERAL_ERROR;
					}
				}
				else
				{
					lenUnzip = lenZipContent;
					bufUnzip = pZipContent;
				}

				if( nxml_parse_buffer( &xml, bufUnzip, lenUnzip ) == NXML_OK )
				{
					nxml_data_t* esgroot;
					esgroot = nxml_find_element( &xml, NULL, "esg:ESGDATA" );
					if( esgroot )
					{
						MXD_UINT32 i;
						MXD_BOOL bServiceTypeGot = FALSE;
						nxml_data_t* esgService = 0;
						esgService = nxml_find_element( &xml, esgroot, "Service" );
						if( !esgService )
						{
							MXD_Free( bufUnzip );
							nxml_empty( &xml );
							ClearUnitListStorage();
							return MXD_GENERAL_ERROR;
						}
						for( i=0; i<service_count; i++ )
						{
							if( pinfo[i].eServiceType == MXD_CMMBSERVICE_TYPE_TV )
								pinfo[i].eServiceType = MXD_CMMBSERVICE_EMPTY;
						}
						while( esgService )
						{
							const char* szServiceName = GetChildText( GetChildElement( esgService, "ServiceName" ), "ServiceNameStr" );
							const char* szServiceClass = GetChildText( esgService, "ServiceClass" );
							MXD_UINT32  idService = GetAttibuteIntValue( esgService, "ServiceID" );

							for( i=0; i<service_count; i++ )
							{
								if( pinfo[i].nServiceID == (MXD_UINT32)idService )
								{
									MXD_Strcpy( (MXD_CHAR*)pinfo[i].szServiceName, szServiceName );
									pinfo[i].nServiceNameLen = MXD_Strlen( (MXD_CHAR*)szServiceName );
									if( MXD_Strlen( szServiceClass ) == 1 )
									{
										switch( *szServiceClass )
										{
										case '1':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_TV;
											break;
										case '2':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_AUDIO;
											break;
										case '3':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_SHORT_VIDEO;
											break;
										case '4':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_FILE_DATA;
											break;
										case '5':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_MANAGE;
											break;
										case '6':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_ESG;
											break;
										case '7':
											pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_STREAM_DATA;
											break;
										}
										bServiceTypeGot = TRUE;
									}
								}
							}
							esgService = esgService->next;
							while( esgService && MXD_Strcmp( esgService->value, "Service" ) != 0 )
								esgService = esgService->next;
						}
						for( i=0; i<service_count; i++ )
						{
							if( bServiceTypeGot )
							{
								if( pinfo[i].eServiceType == MXD_CMMBSERVICE_EMPTY )
									pinfo[i].eServiceType = MXD_CMMBSERVICE_UNKNOWN;
							}
							else
							{
								//avoid wrong esg cause all channel type unknown.
								if( pinfo[i].eServiceType == MXD_CMMBSERVICE_EMPTY )
									pinfo[i].eServiceType = MXD_CMMBSERVICE_TYPE_TV;
							}
						}
					}
				}
				else
				{
					MXD_Free( bufUnzip );
					ClearUnitListStorage();
					return MXD_GENERAL_ERROR;
				}
				MXD_Free( bufUnzip );
				nxml_empty( &xml );
				ClearUnitListStorage();
				return MXD_RTN_OK;
			}
			else
			{
				//todo: unfinished esg block
			}
		}

		pEsgData = GetNextSiblingInList( pEsgData );
	}

	ClearUnitListStorage();
	return MXD_GENERAL_ERROR;
#endif
}

/*******************************************************************************
* \par Description
* This function is used to register the user parameter to demuxer.
*
* \param serviceID	[IN] service id; if the user param is for channel search, this is must be 0;
* \param userParam	[IN] user parameter;
*
* \return   
*    MXD_RTN_OK	-  the service paremeter is filled.
*
* \warning    
*/
MXD_RETURN_CODE DMX_RegisterServiceUserParam( MXD_HANDLE hDevice, MXD_UINT32 serviceID, void* userParam )
{
	MXD_UINT32 j;
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;

	for( j=0; j<pContext->m_nServiceCount; j++ )
	{
		if( pContext->m_pSeviceChannelList[j].nServiceID == serviceID )
		{
			pContext->m_pUserParams[j] = userParam;
			return MXD_RTN_OK;
		}
	}

	return MXD_GENERAL_ERROR;
}

/*******************************************************************************
* \par Description
* This function is used to unregister the user parameter from demuxer.
*
* \param serviceID	[IN] service id; if the user param is for channel search, this is must be 0;
*
* \return   
*    MXD_RTN_OK	-  the service paremeter is deleted.
*
* \warning    
*/
MXD_RETURN_CODE DMX_UnregisterServiceUserParam( MXD_HANDLE hDevice, MXD_UINT32 serviceID )
{
	MXD_UINT32 j;
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;

	for( j=0; j<pContext->m_nServiceCount; j++ )
	{
		if( pContext->m_pSeviceChannelList[j].nServiceID == serviceID )
		{
			pContext->m_pUserParams[j] = 0;
		}
	}

	return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to feed the CLCH frame data, then demuxer will check if there is emergency message in it.
*    If demuxer found an emergency message, the OnEmergencyMsg will be called by demuxer.
*
* \param data	[IN] CLCH frame data;
* \param len	[IN] CLCH frame length;
*
* \return   
*    MXD_DEMUXER_CONTINUE	-  demuxer need more data to complete the emergency message.
*    MXD_RTN_OK	-  demux ok.
*
* \warning    
*/
MXD_RETURN_CODE DMX_FeedControlFrameData( MXD_HANDLE hDevice, MXD_UINT8* data, MXD_INT len )
{
	CB_OnRawFrame			OnRawFrame = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawFrame;
	CB_OnEmergencyMsg		OnEmergencyMsg  = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnEmergencyMsg;
	MXD_UINT32 nServiceID;
	void* pSeviceUserParam;
	MXD_INT32 nEatenBits;
	CMMBUNIT_DEFINE *pFrame, *pCtrlTable;
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;

	if( !Context_GetSrvIdAndUserParam( pContext, 0, &nServiceID, &pSeviceUserParam ) )
	{
		nServiceID = 0;
		pSeviceUserParam = 0;
	}

	if( OnRawFrame )
	{
		//if (OnRawFrame(((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, 0, 0, data, len) == MXD_DEMUXER_STOP)
		//return MXD_RTN_OK;
	}

	InitUnitListStorage();

	pFrame = FrameDecompose( (unsigned char*)data, len, &nEatenBits  );

	if( !pFrame )
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	pCtrlTable = FindUnitFromList( pFrame, "ControlTables" )->m_u1.m_pChildUnitList;
	while( pCtrlTable )
	{
		struct sEmergencyMsgDataSaver* pEmgMsgData;
		CMMBUNIT_DEFINE* pEmergencyMsgSection = NULL;
		if( FindUnitFromList( pCtrlTable, "EmergencyTab" )->m_u1.m_pChildUnitList != 0 )
		{
			pEmergencyMsgSection = FindUnitFromList( FindUnitFromList( pCtrlTable, "EmergencyTab" )->m_u1.m_pChildUnitList, "EmergencyMsgSectionCtn" )->m_u1.m_pChildUnitList;
			if( ! sEmgStg_AddSection( &pContext->m_sEmergencyMsgStorage, FindUnitFromList( pEmergencyMsgSection, "MsgID" )->m_nValue, (MXD_UINT8*)FindUnitFromList( pEmergencyMsgSection, "EmergencyMsgData" )->m_pContent, FindUnitFromList( pEmergencyMsgSection, "MsgLength" )->m_nValue, FindUnitFromList( pEmergencyMsgSection, "CurrentSectionNo" )->m_nValue, FindUnitFromList( pEmergencyMsgSection, "LastSectionNo" )->m_nValue + 1 ) )
			{
				ClearUnitListStorage();
				return MXD_DEMUXER_DATA_REPEAT;
			}
		}
		if( sEmgStg_FindCompleteMsgData( &pContext->m_sEmergencyMsgStorage, &pEmgMsgData ) )
		{
			CMMBUNIT_DEFINE* emgMsg;
			if (NULL == (emgMsg = EmgMsgDecompose(sEmgSaver_GetTotalBuffer(pEmgMsgData), len, &nEatenBits)))
			{
				ClearUnitListStorage();
				return MXD_GENERAL_ERROR;
			}

			DMX_PVT_SendEmergencyMsg( FindUnitFromList( pEmergencyMsgSection, "MsgID" )->m_nValue, emgMsg, ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, OnEmergencyMsg );
		}
		pCtrlTable = GetNextSiblingInList( pCtrlTable );
	}
	ClearUnitListStorage();
	if( sEmgStg_IsBrokenMsgDataExist( &pContext->m_sEmergencyMsgStorage ) )
		return MXD_DEMUXER_CONTINUE;
	else
		return MXD_RTN_OK;
}

/*******************************************************************************
* \par Description
* This function is used to feed the SLCH frame data, then demuxer will check if there is video/audio/data stream in it.
*    If demuxer found video/audio/data stream, the associated callback function will be called by demuxer.
*
* \param data	[IN] SLCH frame data;
* \param len	[IN] SLCH frame length;
*
* \return   
*    MXD_RTN_OK	-  demux ok.
*
* \warning    
*/
MXD_RETURN_CODE DMX_FeedServiceFrameData( MXD_HANDLE hDevice, MXD_UINT32 nServiceID, MXD_UINT8* data, MXD_INT len )
{
	MXD_RETURN_CODE ret = MXD_GENERAL_ERROR;
	CB_OnRawFrame					OnRawFrame = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawFrame;
	CB_OnRawFrameForDescrambling OnRawFrameForDescrambling = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawFrameForDescrambling;
	CB_OnRawSubFrameForDescrambling	OnRawSubFrameForDescrambling  = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnRawSubFrameForDescrambling;
	CB_OnVideoUnit					OnVideoUnit  = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnVideoUnit;
	CB_OnAudioUnit					OnAudioUnit  = ((MIRA_DEVICE_HANDLE*)hDevice)->m_fnCallbacks.OnAudioUnit;

	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)((MIRA_DEVICE_HANDLE*)hDevice)->pDemuxerPrivateStruct;
	void* pSeviceUserParam;
	MXD_INT32 nEatenBits;
	CMMBUNIT_DEFINE *pFrame, *pFrameHeader, *pSubFrame, *pSubFrameLen, *pDataSectionsInfoCtn, *pDisturbFlag;

	MXD_UINT8* pCurrentData, *pData;
	MXD_UINT8* pDescrambledData = NULL;

	MXD_UINT8 nMF_ID, nSubFrameId, nCurrentSubFrame = 1;

	if( !Context_GetUserParam( pContext, nServiceID, &pSeviceUserParam ) )
		pSeviceUserParam = (void*)-1;

	if( !Context_GetMFID( pContext, nServiceID, &nMF_ID, &nSubFrameId ) )
		return MXD_GENERAL_ERROR;

#ifndef USE_EXTERNAL_DEMUXER
	pData = data;
	InitUnitListStorage();
	pFrame = FrameDecompose( pData, len, &nEatenBits  );
	if( !pFrame )
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	pSubFrame = FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList;
	pDisturbFlag = FindUnitFromList( pSubFrame, "DisturbFlag" );

	if( OnRawFrameForDescrambling && pDisturbFlag && pDisturbFlag->m_nValue!=0 )
	{
		MXD_UINT32 nDescrambledSize = len;
		MXD_BOOL bHasEcmInFrame = FALSE; 
		MXD_MEDIA_UNIT_IN_FRAME unitsInFrame;

		if( DMX_PVT_FillMediaUnitFromFrame( pFrame, &unitsInFrame ) == MXD_GENERAL_ERROR )
		{
			ClearUnitListStorage();
			return MXD_GENERAL_ERROR;
		}

		pSubFrame = FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList;
		pDataSectionsInfoCtn = FindUnitFromList( pSubFrame, "DataSectionsInfoCtn" )->m_u1.m_pChildUnitList;
		if( pDataSectionsInfoCtn )
		{
			CMMBUNIT_DEFINE *pECM_Flag;
			pECM_Flag = FindUnitFromList( pDataSectionsInfoCtn, "ECM_Flag" );
			if( pECM_Flag )
				bHasEcmInFrame = pECM_Flag->m_nValue==1 ? TRUE : FALSE;

			if( bHasEcmInFrame )
			{
				//ecm need to send before AV data
				DMX_PVT_SendEcmData( pSubFrame, nServiceID, pSeviceUserParam, hDevice );
			}
		}

		ret = OnRawFrameForDescrambling( ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, nServiceID, pSeviceUserParam, pData, len, unitsInFrame, bHasEcmInFrame, pData, &nDescrambledSize );
		if( ret == MXD_DEMUXER_STOP )
		{
			ClearUnitListStorage();
			return MXD_RTN_OK;
		}

		if( unitsInFrame.pAudioUnitList )
			MXD_Free( unitsInFrame.pAudioUnitList );
		if( unitsInFrame.pVideoUnitList )
			MXD_Free( unitsInFrame.pVideoUnitList );
	}

	pFrameHeader = FindUnitFromList( pFrame, "FrameHeader" )->m_u1.m_pChildUnitList;
	if( NULL == pFrameHeader )
	{
		ClearUnitListStorage();
		return MXD_GENERAL_ERROR;
	}

	pCurrentData = pData + pFrameHeader->m_nContentBitLen/8;
	pSubFrameLen = FindUnitFromList( pFrameHeader, "SubFramesLengthCtn" )->m_u1.m_pChildUnitList;
	pSubFrame = FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList;
	//skip empty subframe
	while( pSubFrameLen->m_nValue == 0 )
	{
		if( nSubFrameId == nCurrentSubFrame && pSubFrameLen->m_nValue == 0 )
			MXD_DebugPrint( "The request sub frame len is 0!\n" );
		nCurrentSubFrame++;
		pSubFrameLen = GetNextSiblingInList( pSubFrameLen );
		pSubFrame = GetNextSiblingInList( pSubFrame );
		if( !pSubFrameLen )
			break;
	}
	while( pSubFrameLen )
	{
		pData = pCurrentData;
		if( OnRawSubFrameForDescrambling && pDisturbFlag && pDisturbFlag->m_nValue!=0 )
		{
			MXD_UINT32 nDescrambledSize = pSubFrameLen->m_nValue;
			pDescrambledData = pData;
			ret = OnRawSubFrameForDescrambling( ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, nServiceID, pSeviceUserParam, pCurrentData, pSubFrameLen->m_nValue, pDescrambledData, &nDescrambledSize );
			if( ret == MXD_DEMUXER_STOP )
			{
				ClearUnitListStorage();
				return MXD_RTN_OK;
			}
		}

		if( NULL == pSubFrame )
		{
			ClearUnitListStorage();
			return MXD_GENERAL_ERROR;
		}

		//only matched subframe will send out.
		if( nCurrentSubFrame == nSubFrameId )
		{
			if( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_bActive && FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList && OnVideoUnit )
				ret = DMX_PVT_SendVideoFrame( pSubFrame, ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, nServiceID, pSeviceUserParam, pContext, OnVideoUnit );

			if( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_bActive && FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList && OnAudioUnit )
				ret = DMX_PVT_SendAudioFrame( pSubFrame, ((MIRA_DEVICE_HANDLE*)hDevice)->nDemodChipIndex, nServiceID, pSeviceUserParam, pContext, OnAudioUnit );

			if( FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_bActive && FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_u1.m_pChildUnitList )
				ret = DMX_PVT_SendDataFrame( pSubFrame, nServiceID, pSeviceUserParam, (MIRA_DEVICE_HANDLE*)hDevice );
		}

		if( nCurrentSubFrame == nSubFrameId )
			break;

		pCurrentData += pSubFrame->m_nContentBitLen/8;

		//skip empty subframe
		do
		{
			nCurrentSubFrame++;
			pSubFrameLen = GetNextSiblingInList( pSubFrameLen );
			if( !pSubFrameLen )
				break;
			if( nSubFrameId == nCurrentSubFrame && pSubFrameLen->m_nValue == 0 )
				MXD_DebugPrint( "The request sub frame len is 0!\n" );
		}while( pSubFrameLen->m_nValue == 0 );
	}

	ClearUnitListStorage();
#endif
	return ret;
}

#define DmxList_Creat( T ) \
	T * m_pHeader_##T = 0; \
	T * m_pLastItem_##T = 0; \
	T * m_pNewItem_##T = 0;

#define DmxList_Destory( T ) \
{ \
	T* pItem_##T; \
	while( m_pHeader_##T ) \
{ \
	pItem_##T = m_pHeader_##T; \
	m_pHeader_##T = pItem_##T->next; \
	MXD_Free( pItem_##T ); \
} \
}

#define DmxList_NewItem( T ) \
{ \
	m_pNewItem_##T = (T*) MXD_Malloc( sizeof(T) ); \
	MXD_Memset( m_pNewItem_##T, 0, sizeof(T) ); \
	if( m_pHeader_##T==0 ) m_pHeader_##T = m_pNewItem_##T; \
	if( m_pLastItem_##T ) m_pLastItem_##T->next = m_pNewItem_##T; \
	m_pLastItem_##T = m_pNewItem_##T; \
};

//micro must at the end of valuable declare block?
#define DmxList_Creat2( T, Y ) \
	T * m_pHeader_##T = 0; \
	T * m_pLastItem_##T = 0; \
	T * m_pNewItem_##T = 0; \
	Y * m_pHeader_##Y = 0; \
	Y * m_pLastItem_##Y = 0; \
	Y * m_pNewItem_##Y = 0;

MXD_RETURN_CODE DMX_PVT_SendEmergencyMsg(  MXD_UINT32 nMsgId, CMMBUNIT_DEFINE* pEmergencyMsg, MXD_UINT8 nDemodChipIndex, CB_OnEmergencyMsg OnEmergencyMsg )
{
	MXD_EMERGENCY_MSG *pMxdEmergencyMsg = 0;
	MXD_EMERGENCY_TRIGGER *pMxdEmergencyTrigger = 0;
	CMMBUNIT_DEFINE* pTriger, *pBody, *pEmergencyMsgDetail, *pExtendEmergencyMsg;
	DmxList_Creat2( MXD_EMERGENCY_EVENT_INFO, MXD_EMERGENCY_AUX_DATA );

	if (NULL != (pTriger = FindUnitFromList(pEmergencyMsg, "EmergencyMsgTrigerCtn")->m_u1.m_pChildUnitList))
	{
		pMxdEmergencyTrigger = (MXD_EMERGENCY_TRIGGER*) MXD_Malloc( sizeof(MXD_EMERGENCY_TRIGGER) );
		pMxdEmergencyTrigger->nMsgId = nMsgId;
		pMxdEmergencyTrigger->nNetId = FindUnitFromList( pTriger, "NetNo" )->m_nValue;
		pMxdEmergencyTrigger->eMsgLevel = (MXD_EMERGENCY_EVENT_LEVEL)FindUnitFromList( pTriger, "Level" )->m_nValue;
		pMxdEmergencyTrigger->eMsgType = (MXD_EMERGENCY_EVENT_TYPE)FindUnitFromList( pTriger, "Type" )->m_nValue;
		pMxdEmergencyTrigger->nNetLevel = FindUnitFromList( pTriger, "NetLevel" )->m_nValue;
		pMxdEmergencyTrigger->tFreqPoint.CentralFreq = FindUnitFromList( pTriger, "NetFreq" )->m_nValue;
		pMxdEmergencyTrigger->tFreqPoint.FreqPointId = FindUnitFromList( pTriger, "NetFreqNo" )->m_nValue;
		pMxdEmergencyTrigger->tFreqPoint.BandWidth = FindUnitFromList( pTriger, "FreqBandWidth" )->m_nValue;
	}

	if (NULL != (pBody = FindUnitFromList(pEmergencyMsg, "EmergencyMsgBodyCtn")->m_u1.m_pChildUnitList))
	{
		pMxdEmergencyMsg = (MXD_EMERGENCY_MSG*) MXD_Malloc( sizeof(MXD_EMERGENCY_MSG) );
		pMxdEmergencyMsg->nId = nMsgId;
		pMxdEmergencyMsg->eType = (MXD_EMERGENCY_EVENT_TYPE) FindUnitFromList( pBody, "Type" )->m_nValue;
		pMxdEmergencyMsg->eLevel = (MXD_EMERGENCY_EVENT_LEVEL) FindUnitFromList( pBody, "Level" )->m_nValue;
		pMxdEmergencyMsg->nCharset = FindUnitFromList( pBody, "Codec" )->m_nValue;
		pMxdEmergencyMsg->nDuration = FindUnitFromList( pBody, "Duration" )->m_nValue;
		pMxdEmergencyMsg->sSendDateTime.MJD_Date = FindUnitFromList( FindUnitFromList( pBody, "SendTime" )->m_u1.m_pChildUnitList, "MJD_Date" )->m_nValue;
		pMxdEmergencyMsg->sSendDateTime.Hour = FindUnitFromList( FindUnitFromList( pBody, "SendTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 16 & 0xff;
		pMxdEmergencyMsg->sSendDateTime.Minute = FindUnitFromList( FindUnitFromList( pBody, "SendTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 8 & 0xff;
		pMxdEmergencyMsg->sSendDateTime.Second = FindUnitFromList( FindUnitFromList( pBody, "SendTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue & 0xff;
		pMxdEmergencyMsg->nLanguageNum = FindUnitFromList( pBody, "LangCount" )->m_nValue;
		pMxdEmergencyMsg->nAuxDataNum = FindUnitFromList( pBody, "ExtendMsgCount" )->m_nValue;

		pEmergencyMsgDetail = FindUnitFromList( pBody, "MsgDetailCtn" )->m_u1.m_pChildUnitList;
		while( pEmergencyMsgDetail )
		{
			MXD_EMERGENCY_EVENT_INFO* pEmgEvtInfo;
			DmxList_NewItem( MXD_EMERGENCY_EVENT_INFO );
			pEmgEvtInfo = m_pNewItem_MXD_EMERGENCY_EVENT_INFO;

			pEmgEvtInfo->eLanguage = (MXD_LANGUAGE_TYPE) FindUnitFromList( pEmergencyMsgDetail, "LangType" )->m_nValue;
			pEmgEvtInfo->nAuxIndex = FindUnitFromList( pEmergencyMsgDetail, "ExtendMsgIndex" )->m_nValue;
			if( FindUnitFromList( pEmergencyMsgDetail, "InstitutionFlag" )->m_nValue == 1 )
			{
				pEmgEvtInfo->nPromulgatorNameLen = FindUnitFromList( FindUnitFromList( pEmergencyMsgDetail, "InstitutionDetailCtn" )->m_u1.m_pChildUnitList, "NameLength" )->m_nValue;
				pEmgEvtInfo->pPromulgatorName = (MXD_UINT8*) FindUnitFromList( FindUnitFromList( pEmergencyMsgDetail, "InstitutionDetailCtn" )->m_u1.m_pChildUnitList, "InstitutionName" )->m_pContent;  //we give our memory out directly to avoid too many memory copy, but it also fobidden user use them when this function exit.
			}
			else
			{
				pEmgEvtInfo->nPromulgatorNameLen = 0;
				pEmgEvtInfo->pPromulgatorName = 0;
			}
			if( FindUnitFromList( pEmergencyMsgDetail, "ReferenceServiceFlag" )->m_nValue )
				pEmgEvtInfo->nReferenceService = FindUnitFromList( pEmergencyMsgDetail, "ReferenceServiceIdc" )->m_nValue;
			else
				pEmgEvtInfo->nReferenceService = 0;
			pEmgEvtInfo->nTextLen = FindUnitFromList( pEmergencyMsgDetail, "TextLength" )->m_nValue;
			pEmgEvtInfo->pText = (MXD_UINT8*) FindUnitFromList( pEmergencyMsgDetail, "MsgText" )->m_pContent; //we give our memory out directly to avoid too many memory copy, but it also fobidden user use them when this function exit.

			pEmergencyMsgDetail = GetNextSiblingInList( pEmergencyMsgDetail );
		}

		pExtendEmergencyMsg = FindUnitFromList( pBody, "ExtendMsgs" )->m_u1.m_pChildUnitList;
		while( pExtendEmergencyMsg )
		{
			MXD_EMERGENCY_AUX_DATA* pEmgAuxData;
			DmxList_NewItem( MXD_EMERGENCY_AUX_DATA );
			pEmgAuxData = m_pNewItem_MXD_EMERGENCY_AUX_DATA;

			pEmgAuxData->eType = (MXD_EMERGENCY_AUX_TYPE) FindUnitFromList( pExtendEmergencyMsg, "DataType" )->m_nValue;
			pEmgAuxData->nLen = FindUnitFromList( pExtendEmergencyMsg, "DataLength" )->m_nValue;
			pEmgAuxData->pData = (MXD_UINT8*) FindUnitFromList( pExtendEmergencyMsg, "ExtendMsgData" )->m_pContent; //we give our memory out directly to avoid too many memory copy, but it also fobidden user use them when this function exit.

			pExtendEmergencyMsg = GetNextSiblingInList( pExtendEmergencyMsg );
		}

		pMxdEmergencyMsg->pEventInfo = m_pHeader_MXD_EMERGENCY_EVENT_INFO;
		pMxdEmergencyMsg->pAuxData = m_pHeader_MXD_EMERGENCY_AUX_DATA;
	}

	if( OnEmergencyMsg )
		OnEmergencyMsg( nDemodChipIndex, pMxdEmergencyMsg, pMxdEmergencyTrigger );

	if( pMxdEmergencyTrigger )
		MXD_Free( pMxdEmergencyTrigger );
	if( pMxdEmergencyMsg )
		MXD_Free( pMxdEmergencyMsg );

	DmxList_Destory( MXD_EMERGENCY_EVENT_INFO );
	DmxList_Destory( MXD_EMERGENCY_AUX_DATA );

	return MXD_RTN_OK;
}

/************************************************************************************************/

#ifndef USE_EXTERNAL_DEMUXER

MXD_RETURN_CODE DMX_PVT_SendVideoFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT8 nDemodChipIndex, MXD_UINT32 nServiceID, void* pSeviceUserParam, MXD_DEMUXER_CONTEXT* pContext, CB_OnVideoUnit OnVideoUnit )
{
	MXD_VIDEO_PARAM *pParam;
	CMMBUNIT_DEFINE* pVideoInfo, *pRawVideoSlice, *pVideoSliceInfo, *pInnoVideoPacket;
	MXD_UINT32 nBufUsedLen = 0;
	MXD_STREAM_VIDEO_FRAME vFrame;
	DmxList_Creat( MXD_VIDEO_PARAM );

	pVideoInfo = FindUnitFromList( pSubFrame, "VideoInfos" )->m_u1.m_pChildUnitList;
	while(pVideoInfo)
	{
		DmxList_NewItem( MXD_VIDEO_PARAM );
		pParam = m_pNewItem_MXD_VIDEO_PARAM;

		pParam->eCodecType = (MXD_VIDEO_CODEC_TYPE) FindUnitFromList( pVideoInfo, "VideoCodecType" )->m_nValue;
		if( FindUnitFromList( pVideoInfo, "FrameRateCtn" )->m_bActive )
			pParam->eFrameFreq = (MXD_VIDEO_FRAME_RATE) FindUnitFromList( pVideoInfo, "FrameRateCtn" )->m_nValue;
		if( FindUnitFromList( pVideoInfo, "BitRateCtn" )->m_bActive )
			pParam->nBitRate = FindUnitFromList( pVideoInfo, "BitRateCtn" )->m_nValue;
		if( FindUnitFromList( pVideoInfo, "ResolutionCtn" )->m_bActive )
		{
			pParam->nHrWidth = FindUnitFromList( FindUnitFromList( pVideoInfo, "ResolutionCtn" )->m_u1.m_pChildUnitList, "ResolutionX" )->m_nValue;
			pParam->nVrWidth = FindUnitFromList( pVideoInfo, "ResolutionY" )->m_nValue;
		}
		if( FindUnitFromList( pVideoInfo, "DisplayCtn" )->m_bActive )
		{
			pParam->nPriority = FindUnitFromList( FindUnitFromList( pVideoInfo, "DisplayCtn" )->m_u1.m_pChildUnitList, "DisplayPriority" )->m_nValue;
			pParam->xStart = FindUnitFromList( FindUnitFromList( pVideoInfo, "DisplayCtn" )->m_u1.m_pChildUnitList, "DisplayStartPointX" )->m_nValue;
			pParam->yStart = FindUnitFromList( FindUnitFromList( pVideoInfo, "DisplayCtn" )->m_u1.m_pChildUnitList, "DisplayStartPointY" )->m_nValue;
		}

		pVideoInfo = GetNextSiblingInList( pVideoInfo );
	}

	pVideoSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "VideoSliceInfos" )->m_u1.m_pChildUnitList;
	pRawVideoSlice = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "RawVideoSlices" )->m_u1.m_pChildUnitList;
	while( pRawVideoSlice && pVideoSliceInfo )
	{
		vFrame.Data = (MXD_UINT8*) pRawVideoSlice->m_pContent;
		vFrame.Size = pRawVideoSlice->m_nContentBitLen/8;
		vFrame.nStreamId = FindUnitFromList( pVideoSliceInfo, "StreamNo" )->m_nValue;
		vFrame.eType = (MXD_VIDEO_FRAME_TYPE) FindUnitFromList( pVideoSliceInfo, "FrameType" )->m_nValue;
		if( FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList && FindUnitFromList( pVideoSliceInfo, "PlayTimeCtn" )->m_u1.m_pChildUnitList )
			vFrame.nStartPlayTime = FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList->m_nValue + FindUnitFromList( pVideoSliceInfo, "PlayTimeCtn" )->m_u1.m_pChildUnitList->m_nValue;

#ifdef SENDOUT_AV_UNIT_IN_RTP_FORMAT
		Context_CheckMediaBuffer( pContext, pRawVideoSlice->m_nContentBitLen/8 );
		DMX_PVT_SendOutRtpVideoUnit( pRawVideoSlice->m_pContent, vFrame, pContext->m_pMediaBuffer, pRawVideoSlice->m_nContentBitLen/8, OnVideoUnit, nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_VIDEO_PARAM );
#else
		if( OnVideoUnit )
			OnVideoUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_VIDEO_PARAM, &vFrame );
#endif //SENDOUT_AV_UNIT_IN_RTP_FORMAT

		pRawVideoSlice = GetNextSiblingInList( pRawVideoSlice );
		pVideoSliceInfo = GetNextSiblingInList( pVideoSliceInfo );
	}

	pInnoVideoPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "InnoVideoPackets" )->m_u1.m_pChildUnitList;
	while( pInnoVideoPacket && pVideoSliceInfo )
	{
		MXD_BOOL bDisableWholeVideoSendOut = FALSE;
		CMMBUNIT_DEFINE* pInnoVideoFragment = FindUnitFromList( pInnoVideoPacket, "InnoVideoFragments" )->m_u1.m_pChildUnitList;
		nBufUsedLen = 0;

		vFrame.nStreamId = FindUnitFromList( pVideoSliceInfo, "StreamNo" )->m_nValue;
		vFrame.eType = (MXD_VIDEO_FRAME_TYPE) FindUnitFromList( pVideoSliceInfo, "FrameType" )->m_nValue;
		if( FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList && FindUnitFromList( pVideoSliceInfo, "PlayTimeCtn" )->m_u1.m_pChildUnitList )
			vFrame.nStartPlayTime = FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList->m_nValue + FindUnitFromList( pVideoSliceInfo, "PlayTimeCtn" )->m_u1.m_pChildUnitList->m_nValue;

		Context_CheckMediaBuffer( pContext, pInnoVideoPacket->m_nContentBitLen/8 );
		while( pInnoVideoFragment )
		{
			if( !FindUnitFromList( pInnoVideoFragment, "Type" )->m_bActive )
			{
				//Encrypted raw H264
				CMMBUNIT_DEFINE* pEncryptH264Raw = FindUnitFromList( pInnoVideoFragment, "RtpNalPacketCtn" )->m_u1.m_pChildUnitList;
#ifdef USE_SMD_CA
				if( pEncryptH264Raw && pEncryptH264Raw->m_nContentBitLen/8 > 7 )
				{
					//in the descremble stream, the first 3 bytes and end 5 bytes are not need for playing.
					MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, pEncryptH264Raw->m_pContent + 2, pEncryptH264Raw->m_nContentBitLen/8 - 2 - 5 );
					nBufUsedLen += pEncryptH264Raw->m_nContentBitLen/8 - 2 - 5;
				}
#else
				if( pEncryptH264Raw )
				{
					MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, pEncryptH264Raw->m_pContent, pEncryptH264Raw->m_nContentBitLen/8 );
					nBufUsedLen += pEncryptH264Raw->m_nContentBitLen/8;
				}
#endif
			}
			else
			{
#ifdef SENDOUT_AV_UNIT_IN_RTP_FORMAT
				nBufUsedLen = DMX_PVT_CreateRtpVideoUnit( FALSE, pInnoVideoFragment, vFrame.nStartPlayTime, pContext->m_pMediaBuffer, FindUnitFromList( pInnoVideoFragment, "InnoPacketLen" )->m_nValue  );

				vFrame.Data = pContext->m_pMediaBuffer;
				vFrame.Size = nBufUsedLen;
				bDisableWholeVideoSendOut = TRUE;

				if( OnVideoUnit )
					OnVideoUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_VIDEO_PARAM, &vFrame );
#else
				MXD_INT32 nVideoType = FindUnitFromList( pInnoVideoFragment, "Type" )->m_nValue;
				if( nVideoType >0 && nVideoType <= 23 ) //H264 raw
				{
					CMMBUNIT_DEFINE* pH264Raw = FindUnitFromList( pInnoVideoFragment, "RtpNalPacketCtn" )->m_u1.m_pChildUnitList;
					if( pH264Raw )
					{
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 1;
						// reconstruct this packet's nal
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0 | (FindUnitFromList( pInnoVideoFragment, "Nal_ref_idc" )->m_nValue << 5) | nVideoType;
						MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, pH264Raw->m_pContent, pH264Raw->m_nContentBitLen/8 );
						nBufUsedLen += pH264Raw->m_nContentBitLen/8;
					}
				}
				else if( nVideoType == 24 ) //STAP_A
				{
					CMMBUNIT_DEFINE* pSTAP_A_Packet = FindUnitFromList( pInnoVideoFragment, "RtpNalPacketCtn" )->m_u1.m_pChildUnitList;
					while( pSTAP_A_Packet )
					{
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 1;
						MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, FindUnitFromList( pSTAP_A_Packet, "NalUnitData" )->m_pContent, FindUnitFromList( pSTAP_A_Packet, "NalUnitData" )->m_nContentBitLen/8 );
						nBufUsedLen += FindUnitFromList( pSTAP_A_Packet, "NalUnitData" )->m_nContentBitLen/8;
						pSTAP_A_Packet = GetNextSiblingInList( pSTAP_A_Packet );
					}
				}
				else if( nVideoType == 28 ) //FU_A
				{
					CMMBUNIT_DEFINE* pFU_A_Packet = FindUnitFromList( pInnoVideoFragment, "RtpNalPacketCtn" )->m_u1.m_pChildUnitList;
					if( FindUnitFromList( pFU_A_Packet, "StartBitIdc" )->m_nValue )
					{
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0;
						pContext->m_pMediaBuffer[nBufUsedLen++] = 1;
						// reconstruct this packet's true nal
						pContext->m_pMediaBuffer[nBufUsedLen++] = 0 | (FindUnitFromList( pInnoVideoFragment, "Nal_ref_idc" )->m_nValue << 5) | FindUnitFromList( pFU_A_Packet, "FU_Type" )->m_nValue;
					}
					MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, FindUnitFromList( pFU_A_Packet, "FU_Data" )->m_pContent, FindUnitFromList( pFU_A_Packet, "FU_Data" )->m_nContentBitLen/8 );
					nBufUsedLen += FindUnitFromList( pFU_A_Packet, "FU_Data" )->m_nContentBitLen/8;
				}
#endif //SENDOUT_AV_UNIT_IN_RTP_FORMAT
			}
			pInnoVideoFragment = GetNextSiblingInList( pInnoVideoFragment );
		}

		if( !bDisableWholeVideoSendOut )
		{
#ifdef SENDOUT_AV_UNIT_IN_RTP_FORMAT
			{
				MXD_UINT8* pNewBuffer;
				pNewBuffer = MXD_Malloc( nBufUsedLen + 20 );
				DMX_PVT_SendOutRtpVideoUnit( pContext->m_pMediaBuffer, vFrame, pNewBuffer, nBufUsedLen, OnVideoUnit, nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_VIDEO_PARAM );
				MXD_Free( pNewBuffer );
			}
#else
			vFrame.Data = pContext->m_pMediaBuffer;
			vFrame.Size = nBufUsedLen;

			if( OnVideoUnit )
				OnVideoUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_VIDEO_PARAM, &vFrame );
#endif //SENDOUT_AV_UNIT_IN_RTP_FORMAT
		}
		pInnoVideoPacket = GetNextSiblingInList( pInnoVideoPacket );
		pVideoSliceInfo = GetNextSiblingInList( pVideoSliceInfo );
	}
	DmxList_Destory( MXD_VIDEO_PARAM );

	return MXD_RTN_OK;
}

MXD_RETURN_CODE DMX_PVT_SendAudioFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT8 nDemodChipIndex, MXD_UINT32 nServiceID, void* pSeviceUserParam, MXD_DEMUXER_CONTEXT* pContext, CB_OnAudioUnit OnAudioUnit )
{
	MXD_UINT32 nBufUsedLen = 0;
	MXD_AUDIO_PARAM *pParam;
	CMMBUNIT_DEFINE* pAudioInfo, *pRawAudioPacket, *pRawAudioSlice, *pAudioSliceInfo, *pInnoAudioPacket, *pRawAacPacket, *pActualAudioSlice=0;
	MXD_STREAM_AUDIO_FRAME aFrame;
	DmxList_Creat( MXD_AUDIO_PARAM );

	pAudioInfo = FindUnitFromList( pSubFrame, "AudioInfos" )->m_u1.m_pChildUnitList;
	while(pAudioInfo)
	{
		DmxList_NewItem( MXD_AUDIO_PARAM )
			pParam = m_pNewItem_MXD_AUDIO_PARAM;

		pParam->eCodecType = (MXD_AUDIO_CODEC_TYPE) FindUnitFromList( pAudioInfo, "AudioCodecType" )->m_nValue;
		if( FindUnitFromList( pAudioInfo, "SampleRateCtn" )->m_u1.m_pChildUnitList )
			pParam->eSampleRate = (MXD_AUDIO_SAMPLERATE) (FindUnitFromList( pAudioInfo, "SampleRateCtn" )->m_u1.m_pChildUnitList+1)->m_nValue;
		if( FindUnitFromList( pAudioInfo, "BitRateCtn" )->m_u1.m_pChildUnitList )
			pParam->nBitRate = (MXD_AUDIO_SAMPLERATE) FindUnitFromList( pAudioInfo, "BitRateCtn" )->m_u1.m_pChildUnitList->m_nValue;
		if( FindUnitFromList( pAudioInfo, "DescriptionCtn" )->m_u1.m_pChildUnitList )
			pParam->eLanguage = (MXD_LANGUAGE_TYPE) FindUnitFromList( pAudioInfo, "DescriptionCtn" )->m_u1.m_pChildUnitList->m_nValue;

		pAudioInfo = GetNextSiblingInList( pAudioInfo );
	}

	pAudioSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "AudioSliceInfos" )->m_u1.m_pChildUnitList;
	pRawAudioPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "RawAudioSlices" )->m_u1.m_pChildUnitList;
	while( pRawAudioPacket && pAudioSliceInfo )
	{
		pRawAudioSlice = FindUnitFromList( pRawAudioPacket, "RawAudioCtn" )->m_u1.m_pChildUnitList;

		while( pRawAudioSlice )
		{
			pActualAudioSlice = FindUnitFromList( pRawAudioSlice, "RawAudio" );
			if( !pActualAudioSlice )
				break;

#ifndef SENDOUT_AV_UNIT_IN_RTP_FORMAT
#ifdef APPEND_ACC_HEADER_IN_DEMUXER
			Context_FillAacFrame( pContext, (MXD_UINT8*)pActualAudioSlice->m_pContent, pActualAudioSlice->m_nContentBitLen/8, 0 );
			aFrame.Data = pContext->m_pMediaBuffer;
			aFrame.Size = pActualAudioSlice->m_nContentBitLen/8 + 7;
#else
			aFrame.Data = (MXD_UINT8*)pActualAudioSlice->m_pContent;
			aFrame.Size = pActualAudioSlice->m_nContentBitLen/8;
#endif
#endif //not SENDOUT_AV_UNIT_IN_RTP_FORMAT

			aFrame.nStreamId = FindUnitFromList( pAudioSliceInfo, "AudioStreamNo" )->m_nValue;
			if( FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList )
				aFrame.nStartPlayTime = FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList->m_nValue + FindUnitFromList( pAudioSliceInfo, "PlayTime" )->m_nValue;

#ifdef SENDOUT_AV_UNIT_IN_RTP_FORMAT
			nBufUsedLen = DMX_PVT_CreateRtpAudioUnit( TRUE, 0, 1, pActualAudioSlice, aFrame.nStartPlayTime, pContext->m_pMediaBuffer, pActualAudioSlice->m_nContentBitLen/8 );
			aFrame.Data = pContext->m_pMediaBuffer;
			aFrame.Size = nBufUsedLen;
#endif //SENDOUT_AV_UNIT_IN_RTP_FORMAT

			if( OnAudioUnit )
				OnAudioUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_AUDIO_PARAM, &aFrame );

			pRawAudioSlice = GetNextSiblingInList( pRawAudioSlice );
		}

		pRawAudioPacket = pRawAudioPacket ? GetNextSiblingInList( pRawAudioPacket ) : NULL;
		pAudioSliceInfo = GetNextSiblingInList( pAudioSliceInfo );
	}

	pAudioSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "AudioSliceInfos" )->m_u1.m_pChildUnitList;
	pInnoAudioPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "InnoAudioPacket" )->m_u1.m_pChildUnitList;
	while( pInnoAudioPacket && pAudioSliceInfo )
	{
		nBufUsedLen = 0;

		aFrame.nStreamId = FindUnitFromList( pAudioSliceInfo, "AudioStreamNo" )->m_nValue;
		if( FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList )
			aFrame.nStartPlayTime = FindUnitFromList( pSubFrame, "StartTimeCtn" )->m_u1.m_pChildUnitList->m_nValue + FindUnitFromList( pAudioSliceInfo, "PlayTime" )->m_nValue;

		pRawAacPacket = FindUnitFromList( pInnoAudioPacket, "RawAacCtn" )->m_u1.m_pChildUnitList;
		while( pRawAacPacket )
		{
			pActualAudioSlice = FindUnitFromList( pRawAacPacket, "RawAacData" );
#ifdef SENDOUT_AV_UNIT_IN_RTP_FORMAT
			nBufUsedLen = DMX_PVT_CreateRtpAudioUnit( FALSE, FindUnitFromList( pInnoAudioPacket, "InnoPacketType" )->m_nValue, FindUnitFromList( pInnoAudioPacket, "InnoEndIdc" )->m_nValue, pActualAudioSlice, aFrame.nStartPlayTime, pContext->m_pMediaBuffer, pActualAudioSlice->m_nContentBitLen/8 );
			aFrame.Data = pContext->m_pMediaBuffer;
			aFrame.Size = nBufUsedLen;
			if( OnAudioUnit )
				OnAudioUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_AUDIO_PARAM, &aFrame );
			aFrame.nStartPlayTime += 900;
#else
#ifdef APPEND_ACC_HEADER_IN_DEMUXER
			Context_FillAacFrame( pContext, (MXD_UINT8*)pActualAudioSlice->m_pContent, pActualAudioSlice->m_nContentBitLen/8, nBufUsedLen );
			nBufUsedLen += pActualAudioSlice->m_nContentBitLen/8 + 7;
#else
			MXD_Memcpy( pContext->m_pMediaBuffer + nBufUsedLen, (MXD_UINT8*)pActualAudioSlice->m_pContent, pActualAudioSlice->m_nContentBitLen/8 );
			//some decoder need seperated aac packet. we send them by calling OnAudioUnit many times.
			aFrame.Data = pContext->m_pMediaBuffer + nBufUsedLen;
			aFrame.Size = pActualAudioSlice->m_nContentBitLen/8;
			if( OnAudioUnit )
				OnAudioUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_AUDIO_PARAM, &aFrame );
			if( GetNextSiblingInList( pAudioSliceInfo ) )
			{
				MXD_UINT32 tm = FindUnitFromList( GetNextSiblingInList( pAudioSliceInfo ), "PlayTime" )->m_nValue - FindUnitFromList( pAudioSliceInfo, "PlayTime" )->m_nValue;
				aFrame.nStartPlayTime += aFrame.Size * tm / FindUnitFromList( pAudioSliceInfo, "AudioSliceLength" )->m_nValue;
			}
			else
			{
				aFrame.nStartPlayTime += 900;
			}
			nBufUsedLen += pActualAudioSlice->m_nContentBitLen/8;
#endif //APPEND_ACC_HEADER_IN_DEMUXER
#endif //SENDOUT_AV_UNIT_IN_RTP_FORMAT

			pRawAacPacket = GetNextSiblingInList( pRawAacPacket );
		}
#ifndef SENDOUT_AV_UNIT_IN_RTP_FORMAT
#ifdef APPEND_ACC_HEADER_IN_DEMUXER
		aFrame.Data = pContext->m_pMediaBuffer;
		aFrame.Size = nBufUsedLen;
		if( OnAudioUnit )
			OnAudioUnit( nDemodChipIndex, nServiceID, pSeviceUserParam, m_pHeader_MXD_AUDIO_PARAM, &aFrame );
#endif //APPEND_ACC_HEADER_IN_DEMUXER
#endif  //not SENDOUT_AV_UNIT_IN_RTP_FORMAT

		pInnoAudioPacket = pInnoAudioPacket ? GetNextSiblingInList( pInnoAudioPacket ) : NULL;
		pAudioSliceInfo = GetNextSiblingInList( pAudioSliceInfo );
	}

	DmxList_Destory( MXD_AUDIO_PARAM );

	return MXD_RTN_OK;
}


MXD_RETURN_CODE DMX_PVT_CheckAndSendEsgData( MIRA_DEVICE_HANDLE* pMiraDevice, MXD_UINT32 nServiceID, void* pSeviceUserParam )
{
	MXD_UINT8 nEsgTypeIDs[20], i, nEsgTypeReceived=0;
	MXD_BOOL bWantedEsgTypeGot = FALSE;
	struct sEsgBlockSaver* pEsgBlockSaver;
	CB_OnEsgData		OnEsgData  = pMiraDevice->m_fnCallbacks.OnEsgData;
	CB_OnCallExtXmlParser OnCallExtXmlParser  = pMiraDevice->m_fnCallbacks.OnCallExtXmlParser;

	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)pMiraDevice->pDemuxerPrivateStruct;
	struct sEsgBlockSaver* pEsgBlockData = pContext->m_sEsgBlockStorage.m_pEsgBlockDataHeader;

	MXD_Memset( nEsgTypeIDs, 0, sizeof(nEsgTypeIDs) );
	while( pEsgBlockData )
	{
		if( sEsgSaver_IsComplete(pEsgBlockData) )
		{
			if( pContext->m_nWantEsgDataTypeID != 0 && pEsgBlockData->m_nBlockTypeId == pContext->m_nWantEsgDataTypeID )
				bWantedEsgTypeGot = TRUE;
			for( i=0; i<pContext->m_nEsgDataTypeIDCount; i++ )
			{
				if( nEsgTypeIDs[i] == pEsgBlockData->m_nBlockTypeId )
					break;
				if( nEsgTypeIDs[i] == 0 )
				{
					nEsgTypeIDs[i] = pEsgBlockData->m_nBlockTypeId;
					nEsgTypeReceived++;
					break;
				}
			}
		}
		pEsgBlockData = pEsgBlockData->next;
	}

	if( nEsgTypeReceived == pContext->m_nEsgDataTypeIDCount || bWantedEsgTypeGot )
	{
		while( sEsgStg_FindCompleteEsgBlock( &pContext->m_sEsgBlockStorage, &pEsgBlockSaver ) )
		{
			nxml_t xml;
			MXD_UINT32 nTotalLen;
			MXD_UINT8* pEsgBuf = sEsgSaver_GetTotalBuffer( pEsgBlockSaver, &nTotalLen );
			MXD_INT8* bufUnzip = (MXD_INT8*)pEsgBuf;
			MXD_INT32 lenUnzip = nTotalLen;

			if( pEsgBlockSaver->m_bZipBlock )
			{
				MXD_Memcpy( &lenUnzip, pEsgBuf + nTotalLen - 4, 4 );
				if( lenUnzip > nTotalLen )
					bufUnzip = (MXD_INT8*) MXD_Malloc( lenUnzip );
				else
					bufUnzip = (MXD_INT8*) MXD_Malloc( nTotalLen*30 );

				lenUnzip = decompress_buf( (MXD_INT8*)pEsgBuf, nTotalLen, bufUnzip, 0 );
				if( lenUnzip == 0 )
				{
					MXD_Free( bufUnzip );
					return MXD_GENERAL_ERROR;
				}
			}

			if( OnCallExtXmlParser )
				OnCallExtXmlParser( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, pEsgBlockSaver->m_nBlockTypeId, bufUnzip, lenUnzip );
			else
			{
				MXD_Memset( &xml, 0, sizeof(nxml_t) );
				if( nxml_parse_buffer( &xml, bufUnzip, lenUnzip ) == NXML_OK )
				{
					nxml_data_t* esgroot;
					esgroot = nxml_find_element( &xml, NULL, "esg:ESGDATA" );
					if( esgroot )
					{
						switch( pEsgBlockSaver->m_nBlockTypeId )
						{
						case 1: //service info
							BuildServiceInfoFromXml( &pContext->m_sEsgDataCtn, esgroot );
							break;
						case 2: //service aux info
							BuildAuxInfoFromXml( &pContext->m_sEsgDataCtn, esgroot );
							break;
						case 3: //schedule info
							BuildSchInfoFromXml( &pContext->m_sEsgDataCtn, esgroot );
							break;
						case 4: //content info
							BuildContentInfoFromXml( &pContext->m_sEsgDataCtn, esgroot );
							break;
						case 5: //service parameter info
							BuildParaInfoFromXml( &pContext->m_sEsgDataCtn, esgroot );
							break;
						}
					}
				}
				nxml_empty( &xml );
			}

			if( pEsgBlockSaver->m_bZipBlock )
				MXD_Free( bufUnzip );
		}
		if( OnEsgData )
			OnEsgData( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, &pContext->m_sEsgDataCtn );
		//clear all esg data
		sEsgStg_Destory( &pContext->m_sEsgBlockStorage );
		RemoveAllAuxInfo( &pContext->m_sEsgDataCtn );
		RemoveAllContentInfo( &pContext->m_sEsgDataCtn );
		RemoveAllParaInfo( &pContext->m_sEsgDataCtn );
		RemoveAllSchInfo( &pContext->m_sEsgDataCtn );
		RemoveAllSrvInfo( &pContext->m_sEsgDataCtn );
	}

	return MXD_RTN_OK;
}

MXD_RETURN_CODE DMX_PVT_SendDataFrame( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT32 nServiceID, void* pSeviceUserParam, MIRA_DEVICE_HANDLE* pMiraDevice )
{
	CB_OnProgramPrompt	OnProgramPrompt  = pMiraDevice->m_fnCallbacks.OnProgramPrompt;
	//CB_OnEsgData		OnEsgData  = pMiraDevice->m_fnCallbacks.OnEsgData;

	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)pMiraDevice->pDemuxerPrivateStruct;

	MXD_RETURN_CODE ret = MXD_RTN_OK;

	CMMBUNIT_DEFINE* pDataSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSliceInfos" )->m_u1.m_pChildUnitList;
	CMMBUNIT_DEFINE* pDataSlice = FindUnitFromList( FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSlices" )->m_u1.m_pChildUnitList;
	while( pDataSliceInfo && pDataSlice )
	{
		CMMBUNIT_DEFINE* pProgramHint = FindUnitFromList( pDataSlice, "ProgramHint" )->m_u1.m_pChildUnitList;
		CMMBUNIT_DEFINE* pEsgDataSection = FindUnitFromList( pDataSlice, "EsgDataSection" )->m_u1.m_pChildUnitList;
		CMMBUNIT_DEFINE* pXpeDataSection = FindUnitFromList( pDataSlice, "XpeDataSection" )->m_u1.m_pChildUnitList;
		CMMBUNIT_DEFINE* pOtherDataSection = FindUnitFromList( pDataSlice, "OtherDataSlice" );

		if( pProgramHint && OnProgramPrompt )
		{
			MXD_PROMPT_INFO *pPrompts;
			MXD_UINT8 nPromptRecCount,i=0;
			CMMBUNIT_DEFINE* pProgramHintRecord = FindUnitFromList( pProgramHint, "ProgramHintRecords" )->m_u1.m_pChildUnitList;

			nPromptRecCount = FindUnitFromList( pProgramHint, "RecordCount" )->m_nValue;
			if( nPromptRecCount > 0 )
			{
				pPrompts = MXD_Malloc( sizeof(MXD_PROMPT_INFO) * nPromptRecCount );

				while( pProgramHintRecord )
				{
					pPrompts[i].Language = ( FindUnitFromList( pProgramHintRecord, "LangType" )->m_nValue == 0x007a686f ? MXD_LANGUAGE_TYPE_CHINESE : MXD_LANGUAGE_TYPE_ENGLISH );
					pPrompts[i].DurationTime = FindUnitFromList( pProgramHintRecord, "Duration" )->m_nValue;
					pPrompts[i].StartTime.MJD_Date = FindUnitFromList( FindUnitFromList( pProgramHintRecord, "StartTime" )->m_u1.m_pChildUnitList, "MJD_Date" )->m_nValue;
					pPrompts[i].StartTime.Hour = FindUnitFromList( FindUnitFromList( pProgramHintRecord, "StartTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 16 & 0xff;
					pPrompts[i].StartTime.Minute = FindUnitFromList( FindUnitFromList( pProgramHintRecord, "StartTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue >> 8 & 0xff;
					pPrompts[i].StartTime.Second = FindUnitFromList( FindUnitFromList( pProgramHintRecord, "StartTime" )->m_u1.m_pChildUnitList, "BCD_Time" )->m_nValue & 0xff;
					pPrompts[i].ThemeLength = FindUnitFromList( pProgramHintRecord, "SubjectLength" )->m_nValue;
					pPrompts[i].ThemeContent = (MXD_INT8*) FindUnitFromList( pProgramHintRecord, "ProgramHintSubject" )->m_pContent; //we give our memory out directly to avoid too many memory copy, but it also fobidden user use them when this function exit.

					pProgramHintRecord = GetNextSiblingInList( pProgramHintRecord );
					i++;
				}

				OnProgramPrompt( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, pPrompts, nPromptRecCount );

				MXD_Free( pPrompts );
				ret = MXD_RTN_OK;
			}
		}

		if( pOtherDataSection->m_bActive && pMiraDevice->m_fnCallbacks.OnEmmPacket )
		{
			MXD_UINT8 nDataType = FindUnitFromList( pDataSliceInfo, "DataSliceType" )->m_nValue;
			MXD_UINT32 i;
			for( i=0; i<pContext->m_nEadtInfoCount; i++ )
			{
				if( pContext->m_pEadtInfoList[i].nEmmDataType == nDataType )
				{
					CB_OnEmmPacket		OnEmmPacket  = pMiraDevice->m_fnCallbacks.OnEmmPacket;
					if( OnEmmPacket )
						OnEmmPacket( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, nDataType, pOtherDataSection->m_pContent, pOtherDataSection->m_nContentBitLen/8 );
				}
			}
		}

		if( pXpeDataSection && (pMiraDevice->m_fnCallbacks.OnStreamXpeData || pMiraDevice->m_fnCallbacks.OnFileXpeData) )
		{
			CMMBUNIT_DEFINE* pXpePayload = FindUnitFromList( pXpeDataSection, "XpePayloadCtn" )->m_u1.m_pChildUnitList;
			if( FindUnitFromList( pXpePayload, "StreamData" )->m_bActive )
			{
				CMMBUNIT_DEFINE* pXpeStreamData = FindUnitFromList( pXpePayload, "StreamData" );
				CB_OnStreamXpeData	OnStreamXpeData  = pMiraDevice->m_fnCallbacks.OnStreamXpeData;
				if( OnStreamXpeData )
					OnStreamXpeData( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, FindUnitFromList( pXpeDataSection, "TransmitNo" )->m_nValue, pXpeStreamData->m_pContent, pXpeStreamData->m_nContentBitLen/8 );
			}

			if( FindUnitFromList( pXpePayload, "FileSliceCtn" )->m_bActive )
			{
				CMMBUNIT_DEFINE* pXpeFileDataList = FindUnitFromList( pXpePayload, "FileSliceCtn" )->m_u1.m_pChildUnitList;
				CMMBUNIT_DEFINE* pXpeFileData = FindUnitFromList( pXpeFileDataList, "FileData" );
				CB_OnFileXpeData	OnFileXpeData  = pMiraDevice->m_fnCallbacks.OnFileXpeData;
				if( OnFileXpeData )
				{
					MXD_FILE_BLOCK fileBlock;
					fileBlock.BlockNo = FindUnitFromList( pXpeFileDataList, "BlockNo" )->m_nValue;
					fileBlock.ResourceID = FindUnitFromList( pXpeFileDataList, "ResourceID" )->m_nValue;
					fileBlock.SliceNo = FindUnitFromList( pXpeFileDataList, "SliceNo" )->m_nValue;
					fileBlock.ReflashNo = FindUnitFromList( pXpeFileDataList, "ReflashNo" )->m_nValue;
					fileBlock.pData = pXpeFileData->m_pContent;
					fileBlock.DataLen = pXpeFileData->m_nContentBitLen/8;
					OnFileXpeData( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, FALSE, FindUnitFromList( pXpeDataSection, "TransmitNo" )->m_nValue, &fileBlock, NULL, 1 );
				}
			}

			if( FindUnitFromList( pXpePayload, "FatSliceCtn" )->m_bActive )
			{
				CMMBUNIT_DEFINE* pXpeFatDataList = FindUnitFromList( pXpePayload, "FatSliceCtn" )->m_u1.m_pChildUnitList;
				CMMBUNIT_DEFINE* pXpeFatData = FindUnitFromList( pXpeFatDataList, "FatData" );
				CB_OnFileXpeData	OnFileXpeData  = pMiraDevice->m_fnCallbacks.OnFileXpeData;
				if( OnFileXpeData )
				{
					MXD_FILEFAT_SLICE fatSlice;
					fatSlice.ResourceID = FindUnitFromList( pXpeFatDataList, "BlockNo" )->m_nValue;
					fatSlice.SliceNo = FindUnitFromList( pXpeFatDataList, "SliceNo" )->m_nValue;
					fatSlice.ZipType = FindUnitFromList( pXpeFatDataList, "ZipType" )->m_nValue;
					fatSlice.LastSliceNo = FindUnitFromList( pXpeFatDataList, "LastSliceNo" )->m_nValue;
					fatSlice.ReflashNo = FindUnitFromList( pXpeFatDataList, "ReflashNo" )->m_nValue;
					fatSlice.pData = pXpeFatData->m_pContent;
					fatSlice.DataLen = pXpeFatData->m_nContentBitLen/8;
					OnFileXpeData( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, TRUE, FindUnitFromList( pXpeDataSection, "TransmitNo" )->m_nValue, NULL, &fatSlice, 1 );
				}
			}
		}

		if( pEsgDataSection && pMiraDevice->m_fnCallbacks.OnEsgData )
		{
			MXD_BOOL bNeedUnzip;
			bNeedUnzip = FindUnitFromList( pEsgDataSection, "ZipType" )->m_nValue;
			//MXD_DebugPrint( "ESG data type %d got.\n", FindUnitFromList( pEsgDataSection, "EsgDataTypeID" )->m_nValue );
			if( ! sEsgStg_AddBlock( &pContext->m_sEsgBlockStorage, FindUnitFromList( pEsgDataSection, "EsgDataBlockID" )->m_nValue, FindUnitFromList( pEsgDataSection, "EsgDataTypeID" )->m_nValue, FindUnitFromList( pEsgDataSection, "EsgDataSlice" )->m_pContent, FindUnitFromList( pEsgDataSection, "EsgDataSlice" )->m_nContentBitLen/8, FindUnitFromList( pEsgDataSection, "BlockNo" )->m_nValue, FindUnitFromList( pEsgDataSection, "BlockCount" )->m_nValue, bNeedUnzip ) )
			{
				MXD_DebugPrint( "Repeated ESG data type %d got.\n", FindUnitFromList( pEsgDataSection, "EsgDataTypeID" )->m_nValue );
			}
			DMX_PVT_CheckAndSendEsgData( pMiraDevice, nServiceID, pSeviceUserParam );
			ret = MXD_RTN_OK;
		}
		pDataSliceInfo = GetNextSiblingInList( pDataSliceInfo );
		pDataSlice = GetNextSiblingInList( pDataSlice );
	}

	return ret;
}

MXD_RETURN_CODE DMX_PVT_SendEcmData( CMMBUNIT_DEFINE* pSubFrame, MXD_UINT32 nServiceID, void* pSeviceUserParam, MIRA_DEVICE_HANDLE* pMiraDevice )
{
	MXD_DEMUXER_CONTEXT* pContext = (MXD_DEMUXER_CONTEXT*)pMiraDevice->pDemuxerPrivateStruct;
	CB_OnEcmPacket		OnEcmPacket  = pMiraDevice->m_fnCallbacks.OnEcmPacket;
	CMMBUNIT_DEFINE* pDataSliceInfo, *pDataSlice;
	MXD_RETURN_CODE ret = MXD_RTN_OK;

	if( !OnEcmPacket )
		return ret;

	pDataSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSliceInfos" )->m_u1.m_pChildUnitList;
	pDataSlice = FindUnitFromList( FindUnitFromList( pSubFrame, "DataSectionCtn" )->m_u1.m_pChildUnitList, "DataSlices" )->m_u1.m_pChildUnitList;
	while( pDataSliceInfo && pDataSlice )
	{
		CMMBUNIT_DEFINE* pOtherDataSection = FindUnitFromList( pDataSlice, "OtherDataSlice" );

		if ( pOtherDataSection && pOtherDataSection->m_bActive)
		{
			MXD_UINT8 nDataType = FindUnitFromList( pDataSliceInfo, "DataSliceType" )->m_nValue;
			MXD_UINT32 i;
			for( i=0; i<pContext->m_nEadtInfoCount; i++ )
			{
				if( pContext->m_pEadtInfoList[i].nEcmDataType == nDataType )
				{
					OnEcmPacket( pMiraDevice->nDemodChipIndex, nServiceID, pSeviceUserParam, nDataType, pOtherDataSection->m_pContent, pOtherDataSection->m_nContentBitLen/8 );
				}
			}
		}
		pDataSliceInfo = GetNextSiblingInList( pDataSliceInfo );
		pDataSlice = GetNextSiblingInList( pDataSlice );
	}

	return ret;
}

MXD_RETURN_CODE DMX_PVT_FillMediaUnitFromFrame( CMMBUNIT_DEFINE* pFrame, MXD_MEDIA_UNIT_IN_FRAME* pMediaUnits )
{
	CMMBUNIT_DEFINE *pSubFrame, *pVideoSliceInfo, *pRawVideoSlice, *pInnoVideoPacket, *pAudioSliceInfo, *pRawAudioPacket, /**pRawAudioSlice,*/ *pInnoAudioPacket, *pRawAacCtn;
	MXD_UINT16 nIndex;
	pSubFrame = FindUnitFromList( pFrame, "SubFrames" )->m_u1.m_pChildUnitList;
	if( !pSubFrame )
		return MXD_GENERAL_ERROR;

	MXD_Memset( pMediaUnits, 0, sizeof(MXD_MEDIA_UNIT_IN_FRAME) );

	if( !FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList )
		return MXD_RTN_OK;

	pMediaUnits->nVideoUnitCount = 0;
	pVideoSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "VideoSliceInfos" )->m_u1.m_pChildUnitList;
	while( pVideoSliceInfo )
	{
		pMediaUnits->nVideoUnitCount++;
		pVideoSliceInfo = GetNextSiblingInList( pVideoSliceInfo );
	}
	if( pMediaUnits->nVideoUnitCount > 0 )
	{
		pMediaUnits->pVideoUnitList = MXD_Malloc( sizeof(MXD_MEDIA_UINT_STRUCT) * pMediaUnits->nVideoUnitCount );
		MXD_Memset( pMediaUnits->pVideoUnitList, 0, sizeof(MXD_MEDIA_UINT_STRUCT) * pMediaUnits->nVideoUnitCount );

		nIndex = 0;
		pRawVideoSlice = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "RawVideoSlices" )->m_u1.m_pChildUnitList;
		while( pRawVideoSlice && nIndex < pMediaUnits->nVideoUnitCount )
		{
			pMediaUnits->pVideoUnitList[nIndex].pContent = pRawVideoSlice->m_pContent;
			pMediaUnits->pVideoUnitList[nIndex++].nContentLen = pRawVideoSlice->m_nContentBitLen/8;
			pRawVideoSlice = GetNextSiblingInList( pRawVideoSlice );
		}

		pInnoVideoPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "VideoSectionCtn" )->m_u1.m_pChildUnitList, "InnoVideoPackets" )->m_u1.m_pChildUnitList;
		while( pInnoVideoPacket && nIndex < pMediaUnits->nVideoUnitCount )
		{
			pMediaUnits->pVideoUnitList[nIndex].pContent = pInnoVideoPacket->m_pContent;
			pMediaUnits->pVideoUnitList[nIndex++].nContentLen = pInnoVideoPacket->m_nContentBitLen/8;
			pInnoVideoPacket = GetNextSiblingInList( pInnoVideoPacket );
		}
	}
	if( !FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList )
		return MXD_RTN_OK;
	pMediaUnits->nAudioUnitCount = 0;
	pAudioSliceInfo = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "AudioSliceInfos" )->m_u1.m_pChildUnitList;
	while( pAudioSliceInfo )
	{
		pMediaUnits->nAudioUnitCount++;
		pAudioSliceInfo = GetNextSiblingInList( pAudioSliceInfo );
	}
	if( pMediaUnits->nAudioUnitCount > 0 )
	{
		pMediaUnits->pAudioUnitList = MXD_Malloc( sizeof(MXD_MEDIA_UINT_STRUCT) * pMediaUnits->nAudioUnitCount );
		MXD_Memset( pMediaUnits->pAudioUnitList, 0, sizeof(MXD_MEDIA_UINT_STRUCT) * pMediaUnits->nAudioUnitCount );

		nIndex = 0;
		pRawAudioPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "RawAudioSlices" )->m_u1.m_pChildUnitList;
		while( pRawAudioPacket && nIndex < pMediaUnits->nAudioUnitCount )
		{
			pMediaUnits->pAudioUnitList[nIndex].pContent = pRawAudioPacket->m_pContent;
			pMediaUnits->pAudioUnitList[nIndex++].nContentLen = pRawAudioPacket->m_nContentBitLen/8;
			pRawAudioPacket = GetNextSiblingInList( pRawAudioPacket );
		}

		pInnoAudioPacket = FindUnitFromList( FindUnitFromList( pSubFrame, "AudioSectionCtn" )->m_u1.m_pChildUnitList, "InnoAudioPacket" )->m_u1.m_pChildUnitList;
		while( pInnoAudioPacket && nIndex < pMediaUnits->nAudioUnitCount )
		{
			pMediaUnits->pAudioUnitList[nIndex].pContent = pInnoAudioPacket->m_pContent;
			pRawAacCtn = FindUnitFromList( pInnoAudioPacket, "RawAacCtn" );
			if( FindUnitFromList( pInnoAudioPacket, "AudioAuHeader" )->m_u1.m_pChildUnitList )
				pMediaUnits->pAudioUnitList[nIndex++].nContentLen = pRawAacCtn->m_nContentBitLen/8 + 12;
			else if( FindUnitFromList( pInnoAudioPacket, "AudioNewHeader" )->m_u1.m_pChildUnitList )
				pMediaUnits->pAudioUnitList[nIndex++].nContentLen = pRawAacCtn->m_nContentBitLen/8 + 8;
			else
				pMediaUnits->pAudioUnitList[nIndex++].nContentLen = pRawAacCtn->m_nContentBitLen/8 + 4;
			pInnoAudioPacket = GetNextSiblingInList( pInnoAudioPacket );
		}
	}
	return MXD_RTN_OK;
}

/** RTP HEADER
0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X|  CC   |M|     PT      |       sequence number       | (4 Bytes)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           timestamp                         | (4 Bytes)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                              SSRC                           | (4 Bytes)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
**/
MXD_UINT32 DMX_PVT_CreateRtpVideoUnit( MXD_BOOL bIsRawH264Video, CMMBUNIT_DEFINE* pInnoVideoFragment, MXD_UINT32 nStartPlayTime, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nPacketLen )
{
	//MXD_UINT32 nLen = 0;
	MXD_UINT8 RtpHeader[12], M, PT;
	static MXD_UINT32 nSequenceVideo = 0;

	if( bIsRawH264Video )
	{
		PT = 0;
		M = 1;
		nPacketLen -= 4; //ignore first 4 bytes [00 00 00 01]
	}
	else
	{
		PT = FindUnitFromList( pInnoVideoFragment, "InnoPacketType" )->m_nValue;
		M = FindUnitFromList( pInnoVideoFragment, "InnoEndIdc" )->m_nValue;
		if( M == 0 || M == 3 )//FRAME_MID or FRAME_BEGIN
			M = 0;
		else  //FRAME_TAIL		= 1 or WHOLE_FRAME		= 3
			M = 1;
	}

	RtpHeader[0] = (2<<6) | 0 | 0 | 0;
	RtpHeader[1] = (M<<7) | (0x60+PT);
	RtpHeader[2] = nSequenceVideo >> 8;
	RtpHeader[3] = nSequenceVideo;
	RtpHeader[4] = nStartPlayTime >> 24;
	RtpHeader[5] = nStartPlayTime >> 16;
	RtpHeader[6] = nStartPlayTime >> 8;
	RtpHeader[7] = nStartPlayTime;
	RtpHeader[8] = 0;
	RtpHeader[9] = 0;
	RtpHeader[10] = 0;
	RtpHeader[11] = 0;

	MXD_Memcpy( m_pMediaBuffer, RtpHeader, 12 );
	if( bIsRawH264Video )
		MXD_Memcpy( m_pMediaBuffer + 12, pInnoVideoFragment->m_pContent + 4, nPacketLen );
	else
		MXD_Memcpy( m_pMediaBuffer + 12, FindUnitFromList( pInnoVideoFragment, "VideoAuHeader" )->m_pContent, nPacketLen );
	nSequenceVideo++;

	return nPacketLen + 12;
}

MXD_UINT32 DMX_PVT_CreateRtpAudioUnit( MXD_BOOL bIsRawHAacAudio, MXD_UINT8 nPacketType, MXD_UINT8 nEndIdc, CMMBUNIT_DEFINE* pRawAacPacket, MXD_UINT32 nStartPlayTime, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nAudioLen )
{
	//  MXD_UINT32 nLen = 0;
	MXD_UINT8 RtpHeader[12], M, PT;
	static MXD_UINT32 nSequenceAudio = 0;

	if( bIsRawHAacAudio )
	{
		PT = 0;
		M = 1;
	}
	else
	{
		PT = nPacketType;
		M = nEndIdc;
		if( M == 0 || M == 3 )//FRAME_MID or FRAME_BEGIN
			M = 0;
		else  //FRAME_TAIL		= 1 or WHOLE_FRAME		= 3
			M = 1;
	}

	RtpHeader[0] = (2<<6) | 0 | 0 | 0;
	RtpHeader[1] = (1<<7) | 97;
	RtpHeader[2] = nSequenceAudio >> 8;
	RtpHeader[3] = nSequenceAudio;
	RtpHeader[4] = nStartPlayTime >> 24;
	RtpHeader[5] = nStartPlayTime >> 16;
	RtpHeader[6] = nStartPlayTime >> 8;
	RtpHeader[7] = nStartPlayTime;
	RtpHeader[8] = 0;
	RtpHeader[9] = 0;
	RtpHeader[10] = 0;
	RtpHeader[11] = 0;

	nSequenceAudio++;
	MXD_Memcpy( m_pMediaBuffer, RtpHeader, 12 );
	if( bIsRawHAacAudio )
	{
		MXD_Memcpy( m_pMediaBuffer + 12, pRawAacPacket->m_pContent - 1, pRawAacPacket->m_nContentBitLen/8 + 1 );

		return pRawAacPacket->m_nContentBitLen/8 + 1 + 12;
	}
	else
	{
		m_pMediaBuffer[12] = nAudioLen;
		MXD_Memcpy( m_pMediaBuffer + 13, pRawAacPacket->m_pContent, nAudioLen );

		return nAudioLen + 1 + 12;
	}
}

MXD_UINT32 DMX_PVT_SendOutRtpVideoUnit(MXD_UINT8* pRawVideoContent, MXD_STREAM_VIDEO_FRAME vFrame, MXD_UINT8* m_pMediaBuffer, MXD_UINT32 nPacketLen, CB_OnVideoUnit OnVideoUnit, MXD_UINT8 nDemodChipIndex, MXD_UINT32 serviceID, void* userParam, MXD_VIDEO_PARAM *tParam)
{
	MXD_UINT32 i, nUnitEndMark[100], nUnitEndCount = 0;
	MXD_UINT8* pNowMediaBuffer = m_pMediaBuffer;
	MXD_UINT32 nStartPlayTime = vFrame.nStartPlayTime;
	MXD_UINT8  fu_hdr_len = 1;
	MXD_UINT32  nalSize = 0;
	MXD_UINT32 offset = 0;
	MXD_BOOL firstFragment = TRUE;
	MXD_UINT8 NALhdr = 0;
	MXD_UINT8 NALtype = 0;
	//MXD_UINT8* m_ptempMediaBuffer = NULL;
	static MXD_UINT32 nSequenceVideo = 0;
	MXD_UINT8 RtpHeader[12], M, PT;
	MXD_BOOL bH264Header = TRUE;

	for (i = 2; i < nPacketLen - 3;  i++)
	{
		if (pRawVideoContent[i-1] == 0 && pRawVideoContent[i] == 0 && pRawVideoContent[i+1] == 0 && pRawVideoContent[i+2] == 1)
		{
			nUnitEndMark[nUnitEndCount++] = i - 1;
			bH264Header = FALSE;
		}
		else if (pRawVideoContent[i] == 0 && pRawVideoContent[i+1] == 0 && pRawVideoContent[i+2] == 1)
		{
			nUnitEndMark[nUnitEndCount++] = i;
			bH264Header = TRUE;
		}
		else {}

	}

	if (0 == nUnitEndCount)
	{
		if (pRawVideoContent[0] == 0 && pRawVideoContent[1] == 0 && pRawVideoContent[2] == 0 && pRawVideoContent[3] == 1)
			bH264Header = FALSE;
	}

	nUnitEndCount++;
	nUnitEndMark[nUnitEndCount-1] = nPacketLen;

	for (i = 0; i < nUnitEndCount; i++)
	{
		PT = RTP_PT_H264;
		M = 1;
		RtpHeader[0] = (2 << 6) | 0 | 0 | 0;
		// RtpHeader[1] = (M << 7) | (0x60 + PT);
		//  RtpHeader[2] = nSequenceVideo >> 8;
		//  RtpHeader[3] = nSequenceVideo;
		RtpHeader[4] = nStartPlayTime >> 24;
		RtpHeader[5] = nStartPlayTime >> 16;
		RtpHeader[6] = nStartPlayTime >> 8;
		RtpHeader[7] = nStartPlayTime;
		RtpHeader[8] = 0;
		RtpHeader[9] = 0;
		RtpHeader[10] = 0;
		RtpHeader[11] = 0;

		if (i == 0)
		{
			if (!bH264Header)
				nalSize = nUnitEndMark[i] - 4;
			else
				nalSize = nUnitEndMark[i] - 3;
		}
		else
		{
			if (!bH264Header)
				nalSize = nUnitEndMark[i] - nUnitEndMark[i-1] - 4 ;
			else
				nalSize = nUnitEndMark[i] - nUnitEndMark[i-1] - 3;
		}

		firstFragment = TRUE;
		if (!bH264Header)
			NALhdr = *(pRawVideoContent + 4);
		else
			NALhdr = *(pRawVideoContent + 3);
		//MXD_DebugPrint("\nVIDE0[%x,%x,%x,%x--%x]", *(pRawVideoContent), *(pRawVideoContent + 1) , *(pRawVideoContent + 2), *(pRawVideoContent + 3), *(pRawVideoContent + 4));
		NALtype = NALhdr & 0x1f;

		while (nalSize > 0)
		{
			RtpHeader[2] = nSequenceVideo >> 8;
			RtpHeader[3] = nSequenceVideo;
			nSequenceVideo++;

			if (nalSize <= NAL_FRAG_THRESH)
			{
				//==============================================
				//Single NAL or last fragment of FU-A
				//==============================================
				M = 1;
				RtpHeader[1] = (M << 7) | PT;
				MXD_Memcpy(pNowMediaBuffer, RtpHeader, 12);
				pNowMediaBuffer += RTP_HDR_LEN;
				vFrame.Size =  nalSize + RTP_HDR_LEN + fu_hdr_len;

				if (fu_hdr_len == 2)
				{
					//  vFrame.Size =  nalSize + RTP_HDR_LEN + fu_hdr_len;
					//==============================================
					//Last fragment of FU-A
					//==============================================
					*pNowMediaBuffer = 0x00 | (NALhdr & 0x60) | 28;     //FU indicator
					*(pNowMediaBuffer + 1) = 0x40 | NALtype;                //FU header
				}
				else
				{
					vFrame.Size =  nalSize + RTP_HDR_LEN + fu_hdr_len - 1;
					*pNowMediaBuffer = NALhdr;              //NAL Header
				}

				if (!bH264Header)
					MXD_Memcpy(pNowMediaBuffer + fu_hdr_len, pRawVideoContent + offset + 4 + 1, nalSize);
				else
					MXD_Memcpy(pNowMediaBuffer + fu_hdr_len, pRawVideoContent + offset + 3 + 1, nalSize);
				//copy naltoRtp
				nalSize = 0;
				offset = 0;
			}
			else
			{
				//==============================================
				//FU-A (not the last fragment though)
				//==============================================
				M = 0;  // set M bit
				RtpHeader[1] = (M << 7) | PT;
				MXD_Memcpy(pNowMediaBuffer, RtpHeader, 12);
				pNowMediaBuffer += RTP_HDR_LEN;
				fu_hdr_len = 2;
				vFrame.Size = (NAL_FRAG_THRESH - fu_hdr_len) + RTP_HDR_LEN + fu_hdr_len;
				*pNowMediaBuffer = 0x00 | (NALhdr & 0x60) | 28;             //FU indicator
				*(pNowMediaBuffer + 1) = ((firstFragment) ? 0x80 : 0x00) | NALtype;  //FU header
				if (!bH264Header)
					MXD_Memcpy(pNowMediaBuffer + fu_hdr_len, pRawVideoContent + offset + 4 + 1,  NAL_FRAG_THRESH - fu_hdr_len);
				else
					MXD_Memcpy(pNowMediaBuffer + fu_hdr_len, pRawVideoContent + offset + 3 + 1,  NAL_FRAG_THRESH - fu_hdr_len);

				if (firstFragment)
				{
					nalSize -= (NAL_FRAG_THRESH - fu_hdr_len + 1);
					offset += (NAL_FRAG_THRESH - fu_hdr_len);
				}
				else
				{
					nalSize -= (NAL_FRAG_THRESH - fu_hdr_len);
					offset += (NAL_FRAG_THRESH - fu_hdr_len);
				}

				firstFragment = FALSE;
			}

			vFrame.Data = pNowMediaBuffer - RTP_HDR_LEN;

			if (OnVideoUnit)
				OnVideoUnit(nDemodChipIndex, serviceID, userParam, tParam, &vFrame);
		}

		if (i == 0)
		{
			pRawVideoContent += nUnitEndMark[i];
		}
		else
		{
			pRawVideoContent += nUnitEndMark[i] - nUnitEndMark[i-1];
		}

	}

	return pNowMediaBuffer - m_pMediaBuffer;
}

#endif //USE_EXTERNAL_DEMUXER


void sEmgSaver_Init( struct sEmergencyMsgDataSaver *pThis )
{
	MXD_Memset( pThis, 0, sizeof(struct sEmergencyMsgDataSaver) );
}

void sEmgSaver_Destory( struct sEmergencyMsgDataSaver *pThis )
{
	MXD_UINT8 i;
	if( pThis->next != 0 )
		sEmgSaver_Destory( pThis->next );
	if( pThis->m_pMsgSectionBodyLenArray != 0 )
		MXD_Free( pThis->m_pMsgSectionBodyLenArray );
	if( pThis->m_pMsgSectionBodyArray != 0 )
	{
		for( i=0; i<pThis->m_nTotalSection; i++ )
		{
			if( pThis->m_pMsgSectionBodyArray[i] != 0 )
				MXD_Free( pThis->m_pMsgSectionBodyArray[i] );
		}
		MXD_Free( pThis->m_pMsgSectionBodyArray );
	}
	if( pThis->m_pTotalBuf != 0 )
		MXD_Free( pThis->m_pTotalBuf );
	MXD_Free( pThis );
}

MXD_BOOL sEmgSaver_IsComplete( struct sEmergencyMsgDataSaver *pThis )
{
	MXD_UINT8 i;
	for( i=0; i<pThis->m_nTotalSection; i++ )
	{
		if( pThis->m_pMsgSectionBodyLenArray[i] == 0 )
			return FALSE;
	}
	return TRUE;
}

MXD_UINT8* sEmgSaver_GetTotalBuffer( struct sEmergencyMsgDataSaver *pThis )
{
	MXD_UINT8 *pBuf;
	MXD_UINT8 i;
	if( !sEmgSaver_IsComplete(pThis) )
		return 0;

	if( !pThis->m_pTotalBuf )
	{
		MXD_UINT32 nTotalLen = 0;
		for( i=0; i<pThis->m_nTotalSection; i++ )
		{
			nTotalLen += pThis->m_pMsgSectionBodyLenArray[i];
		}
		pThis->m_pTotalBuf = (MXD_UINT8*) MXD_Malloc( nTotalLen );
	}

	pBuf = pThis->m_pTotalBuf;
	for( i=0; i<pThis->m_nTotalSection; i++ )
	{
		MXD_Memcpy( pBuf, pThis->m_pMsgSectionBodyArray[i], pThis->m_pMsgSectionBodyLenArray[i] );
		pBuf += pThis->m_pMsgSectionBodyLenArray[i];
	}
	return pThis->m_pTotalBuf;
}

void sEmgStg_Init( struct sEmergencyMsgStorage* pThis )
{
	pThis->m_pEmergencyMsgDataHeader = 0;
	pThis->m_pEmergencyMsgDataTail = 0;
}

void sEmgStg_Destory( struct sEmergencyMsgStorage* pThis )
{
	if( pThis->m_pEmergencyMsgDataHeader )
		sEmgSaver_Destory( pThis->m_pEmergencyMsgDataHeader );
}

struct sEmergencyMsgDataSaver* sEmgStg_GetMsgData( struct sEmergencyMsgStorage* pThis, MXD_UINT32 nMsgId )
{
	struct sEmergencyMsgDataSaver* pEmergencyMsgData = pThis->m_pEmergencyMsgDataHeader;
	while( pEmergencyMsgData )
	{
		if( pEmergencyMsgData->m_nMsgId == nMsgId )
			return pEmergencyMsgData;
		pEmergencyMsgData = pEmergencyMsgData->next;
	}
	return 0;
}

MXD_BOOL sEmgStg_AddSection( struct sEmergencyMsgStorage* pThis, MXD_UINT32 nMsgId, MXD_UINT8* pMsgData, MXD_UINT32 nSectionLen, MXD_UINT8 nCurrentSection, MXD_UINT8 nTotalSection )
{
	struct sEmergencyMsgDataSaver* pEmergencyMsgData = sEmgStg_GetMsgData( pThis, nMsgId );
	if( !pEmergencyMsgData )
	{
		pEmergencyMsgData = (struct sEmergencyMsgDataSaver*)MXD_Malloc( sizeof(struct sEmergencyMsgDataSaver) );
		sEmgSaver_Init( pEmergencyMsgData );
		pEmergencyMsgData->m_nTotalSection = nTotalSection;
		pEmergencyMsgData->m_nMsgId = nMsgId;
		pEmergencyMsgData->m_pMsgSectionBodyLenArray = (MXD_UINT32*) MXD_Malloc( sizeof(MXD_UINT32)*nTotalSection );
		pEmergencyMsgData->m_pMsgSectionBodyArray = (MXD_UINT8**) MXD_Malloc( sizeof(MXD_UINT8*)*nTotalSection );
		MXD_Memset( pEmergencyMsgData->m_pMsgSectionBodyLenArray, 0, sizeof(MXD_UINT32)*nTotalSection );
		if( pThis->m_pEmergencyMsgDataTail )
		{
			pThis->m_pEmergencyMsgDataTail->next = pEmergencyMsgData;
			pThis->m_pEmergencyMsgDataTail = pEmergencyMsgData;
		}
	}
	else if( nCurrentSection >= pEmergencyMsgData->m_nTotalSection )
		return FALSE; // todo:
	else if( pEmergencyMsgData->m_pMsgSectionBodyLenArray[ nCurrentSection ] != 0 )
		return FALSE; //repeat data got!
	pEmergencyMsgData->m_pMsgSectionBodyLenArray[ nCurrentSection ] = nSectionLen;
	pEmergencyMsgData->m_pMsgSectionBodyArray[ nCurrentSection ] = (MXD_UINT8*) MXD_Malloc( nSectionLen );
	MXD_Memcpy( pEmergencyMsgData->m_pMsgSectionBodyArray[ nCurrentSection ], pMsgData, nSectionLen );

	if( ! pThis->m_pEmergencyMsgDataHeader )
		pThis->m_pEmergencyMsgDataHeader = pEmergencyMsgData;
	if( ! pThis->m_pEmergencyMsgDataTail )
		pThis->m_pEmergencyMsgDataTail = pEmergencyMsgData;
	return TRUE;
}

MXD_BOOL sEmgStg_FindCompleteMsgData( struct sEmergencyMsgStorage* pThis, struct sEmergencyMsgDataSaver** ppEmgMsgData )
{
	struct sEmergencyMsgDataSaver* pEmergencyMsgData = pThis->m_pEmergencyMsgDataHeader;
	while( pEmergencyMsgData )
	{
		if( sEmgSaver_IsComplete(pEmergencyMsgData) && pEmergencyMsgData->m_pTotalBuf == 0 ) //only msg not used.
		{
			*ppEmgMsgData = pEmergencyMsgData;
			return TRUE;
		}
		pEmergencyMsgData = pEmergencyMsgData->next;
	}
	*ppEmgMsgData = 0;
	return FALSE;
}

MXD_BOOL sEmgStg_IsBrokenMsgDataExist( struct sEmergencyMsgStorage* pThis )
{
	struct sEmergencyMsgDataSaver* pEmergencyMsgData = pThis->m_pEmergencyMsgDataHeader;
	while( pEmergencyMsgData )
	{
		if( !sEmgSaver_IsComplete(pEmergencyMsgData) )
			return TRUE;
		pEmergencyMsgData = pEmergencyMsgData->next;
	}
	return FALSE;
}

#ifndef USE_EXTERNAL_DEMUXER

MXD_BOOL BuildServiceInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* esgroot )
{
	MXD_UINT32 nCount = CountChildren( esgroot, "Service" );

	MXD_UINT32 nIndex = CreateOrExpandInfoArray( &pEsgData->nSerInfoCount, (void**)&pEsgData->pSerInfoArray, sizeof(MXD_SERVICE_INFO), nCount );

	nxml_data_t* pNode = 0;
	for( pNode = GetChildElement( esgroot, "Service" ); pNode; pNode = GetNextElement( pNode, "Service" ), nIndex++ )
	{
		MXD_UINT32 nSubIndex;
		nxml_data_t* pSubNode = 0;
		pEsgData->pSerInfoArray[nIndex].ServiceId = (MXD_UINT16) GetAttibuteIntValue( pNode, "ServiceID" );

		pEsgData->pSerInfoArray[nIndex].Class = (MXD_SERVICE_CLASS) GetChildIntValue( pNode, "ServiceClass" );

		pEsgData->pSerInfoArray[nIndex].Genre = GetChildIntValue( pNode, "ServiceGenre" );

		pEsgData->pSerInfoArray[nIndex].ParaId = GetChildIntValue( pNode, "ServiceParaID" );

		pEsgData->pSerInfoArray[nIndex].ForFree = GetChildBoolValue2( pNode, "forFree" );

		pEsgData->pSerInfoArray[nIndex].CaDescriptor = (MXD_UINT8*) CloneChildText( pNode, "CADescriptor" );

		nCount = CountChildren( pNode, "ServiceName" );
		pEsgData->pSerInfoArray[nIndex].nServiceNameCount = nCount;
		pEsgData->pSerInfoArray[nIndex].pServiceNameArray = (MXD_SERVICE_NAME*) MXD_Malloc( sizeof(MXD_SERVICE_NAME)*nCount );
		MXD_Memset( pEsgData->pSerInfoArray[nIndex].pServiceNameArray, 0, sizeof(MXD_SERVICE_NAME)*nCount );

		for( pSubNode = GetChildElement( pNode, "ServiceName" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "ServiceName" ), nSubIndex++ )
		{
			pEsgData->pSerInfoArray[nIndex].pServiceNameArray[nSubIndex].Lang = (MXD_LANGUAGE_TYPE)GetAttibuteLangValue( pSubNode, "lang" );

			pEsgData->pSerInfoArray[nIndex].pServiceNameArray[nSubIndex].Name = CloneChildText( pSubNode, "ServiceNameStr" );
		}
	}
	return TRUE;
}

MXD_BOOL BuildSchInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* esgroot )
{
	MXD_UINT32 nCount = CountChildren( esgroot, "Schedule" );

	MXD_UINT32 nIndex = CreateOrExpandInfoArray( &pEsgData->nSchInfoCount, (void**)&pEsgData->pSchInfoArray, sizeof(MXD_SCHEDULE_INFO), nCount );

	nxml_data_t* pNode = 0;
	for( pNode = GetChildElement( esgroot, "Schedule" ); pNode; pNode = GetNextElement( pNode, "Schedule" ), nIndex++ )
	{
		MXD_UINT32 nSubIndex;
		nxml_data_t* pSubNode , *pStartTimeNode = 0;
		pEsgData->pSchInfoArray[nIndex].ServiceId = GetAttibuteIntValue( pNode, "ScheduleID" );

		pEsgData->pSchInfoArray[nIndex].ServiceId = GetChildIntValue( pNode, "ServiceID" );

		pEsgData->pSchInfoArray[nIndex].ContentId = GetChildIntValue( pNode, "ContentID" );

		pEsgData->pSchInfoArray[nIndex].ForFree = GetChildBoolValue2( pNode, "forFree" );

		pEsgData->pSchInfoArray[nIndex].Live = GetChildBoolValue( pNode, "Live" );

		pEsgData->pSchInfoArray[nIndex].Repeat = GetChildBoolValue( pNode, "Repeat" );

		pEsgData->pSchInfoArray[nIndex].CaDescriptor = (MXD_UINT8*) CloneChildText( pNode, "CADescriptor" );

		if(NULL != (pStartTimeNode = GetChildElement(pNode, "StartTime")) )
		{
			pEsgData->pSchInfoArray[nIndex].StartTime.Date = CloneChildText( pStartTimeNode, "Date" );
			pEsgData->pSchInfoArray[nIndex].StartTime.Time = CloneChildText( pStartTimeNode, "Time" );
		}

		nCount = CountChildren( pNode, "Title" );
		pEsgData->pSchInfoArray[nIndex].nTitleCount = nCount;
		pEsgData->pSchInfoArray[nIndex].pTitleArray = (MXD_SCHEDULE_TITLE*) MXD_Malloc( sizeof(MXD_SCHEDULE_TITLE)*nCount );
		MXD_Memset( pEsgData->pSchInfoArray[nIndex].pTitleArray, 0, sizeof(MXD_SCHEDULE_TITLE)*nCount );

		for( pSubNode = GetChildElement( pNode, "Title" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "Title" ), nSubIndex++ )
		{
			pEsgData->pSchInfoArray[nIndex].pTitleArray[nSubIndex].Lang = (MXD_LANGUAGE_TYPE)GetAttibuteLangValue( pSubNode, "lang" );

			pEsgData->pSchInfoArray[nIndex].pTitleArray[nSubIndex].Str = (MXD_INT8*)CloneChildText( pSubNode, "TitleStr" );
		}
	}
	return TRUE;
}

MXD_BOOL BuildParaInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* esgroot )
{
	MXD_UINT32 nCount = CountChildren( esgroot, "ServicePara" );

	MXD_UINT32 nIndex = CreateOrExpandInfoArray( &pEsgData->nParaInfoCount, (void**)&pEsgData->pParaInfoArray, sizeof(MXD_SERVICE_PARA), nCount );

	nxml_data_t* pNode = 0;
	for( pNode = GetChildElement( esgroot, "ServicePara" ); pNode; pNode = GetNextElement( pNode, "ServicePara" ), nIndex++ )
	{
		pEsgData->pParaInfoArray[nIndex].ParaId = GetAttibuteIntValue( pNode, "ServiceParaID" );
		//todo!
	}
	return TRUE;
}

MXD_BOOL BuildContentInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* esgroot )
{
	MXD_UINT32 nCount = CountChildren( esgroot, "Content" );

	MXD_UINT32 nIndex = CreateOrExpandInfoArray( &pEsgData->nContentInfoCount, (void**)&pEsgData->pContentInfoArray, sizeof(MXD_CONTENT_INFO), nCount );

	nxml_data_t* pNode = 0;
	for( pNode = GetChildElement( esgroot, "Content" ); pNode; pNode = GetNextElement( pNode, "Content" ), nIndex++ )
	{
		pEsgData->pContentInfoArray[nIndex].ContentId = GetAttibuteIntValue( pNode, "ContentID" );

		pEsgData->pContentInfoArray[nIndex].ContentClass = (MXD_INT8*)CloneChildText( pNode, "ContentClass" );

		pEsgData->pContentInfoArray[nIndex].Genre = GetChildIntValue( pNode, "ContentGenre" );

		pEsgData->pContentInfoArray[nIndex].Duration = GetChildIntValue( pNode, "Duration" );

		pEsgData->pContentInfoArray[nIndex].ServiceParaId = GetChildIntValue( pNode, "ServiceParaID" );

		pEsgData->pContentInfoArray[nIndex].ExtentInfo = (MXD_INT8*)CloneChildText( pNode, "ExtentInfo" );

		nCount = CountChildren( pNode, "Media" );
		if( nCount )
		{
			MXD_UINT32 nSubIndex;
			nxml_data_t* pSubNode = 0;

			pEsgData->pContentInfoArray[nIndex].nMediaCount = nCount;
			pEsgData->pContentInfoArray[nIndex].pMediaArray = (MXD_MEDIA*) MXD_Malloc( sizeof(MXD_MEDIA)*nCount );
			MXD_Memset( pEsgData->pContentInfoArray[nIndex].pMediaArray, 0, sizeof(MXD_MEDIA)*nCount );

			for( pSubNode = GetChildElement( pNode, "Media" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "Media" ), nSubIndex++ )
			{
				pEsgData->pContentInfoArray[nIndex].pMediaArray[nSubIndex].Usage = (MXD_MEDIA_USAGE) GetChildIntValue(pSubNode, "Usage");

				pEsgData->pContentInfoArray[nIndex].pMediaArray[nSubIndex].Type = (MXD_INT8 *) CloneChildText(pSubNode, "ContentType");

				pEsgData->pContentInfoArray[nIndex].pMediaArray[nSubIndex].Data = (MXD_INT8 *) CloneChildText(pSubNode, "MediaData");
			}
		}

		nCount = CountChildren( pNode, "ContentLanguageSpecial" );
		if( nCount )
		{
			MXD_UINT32 nSubIndex;
			nxml_data_t* pSubNode = 0;

			pEsgData->pContentInfoArray[nIndex].nTextCount = nCount;
			pEsgData->pContentInfoArray[nIndex].pTextArray = (MXD_CONTENT_TEXT*) MXD_Malloc(sizeof(MXD_CONTENT_TEXT)*nCount );
			MXD_Memset( pEsgData->pContentInfoArray[nIndex].pTextArray, 0, sizeof(MXD_CONTENT_TEXT)*nCount );

			for( pSubNode = GetChildElement( pNode, "ContentLanguageSpecial" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "ContentLanguageSpecial" ), nSubIndex++ )
			{
				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].Lang = (MXD_LANGUAGE_TYPE)GetAttibuteLangValue( pSubNode, "lang" );

				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].Title = (MXD_INT8 *) CloneChildText(pSubNode, "Title");

				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].DigestInfo = (MXD_INT8 *) CloneChildText(pSubNode, "DigestInfo");

				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].Keyword = (MXD_INT8 *) CloneChildText(pSubNode, "Keyword");

				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].AudioLang = (MXD_LANGUAGE_TYPE)GetChildLangValue(pSubNode, "AudioLang");

				pEsgData->pContentInfoArray[nIndex].pTextArray[nSubIndex].SubTitleLang = (MXD_LANGUAGE_TYPE)GetChildLangValue(pSubNode, "SubtitleLang");
			}
		}
	}
	return TRUE;
}

MXD_BOOL BuildAuxInfoFromXml( MXD_ESG_DATA* pEsgData, nxml_data_t* esgroot )
{
	MXD_UINT32 nCount = CountChildren( esgroot, "ServiceAux" );

	MXD_UINT32 nIndex = CreateOrExpandInfoArray( &pEsgData->nAuxInfoCount, (void**)&pEsgData->pAuxInfoArray, sizeof(MXD_SERVICE_AUX_INFO), nCount );

	nxml_data_t* pNode = 0;
	for( pNode = GetChildElement( esgroot, "ServiceAux" ); pNode; pNode = GetNextElement( pNode, "ServiceAux" ), nIndex++ )
	{
		pEsgData->pAuxInfoArray[nIndex].ServiceId = GetAttibuteIntValue( pNode, "ServiceID" );

		pEsgData->pAuxInfoArray[nIndex].ExtentInfo = (MXD_INT8*)CloneChildText( pNode, "ExtentInfo" );

		nCount = CountChildren( pNode, "Media" );
		if( nCount )
		{
			MXD_UINT32 nSubIndex;
			nxml_data_t* pSubNode = 0;

			pEsgData->pAuxInfoArray[nIndex].nMediaCount = nCount;
			pEsgData->pAuxInfoArray[nIndex].pMediaArray = (MXD_MEDIA*) MXD_Malloc( sizeof(MXD_MEDIA)*nCount );
			MXD_Memset( pEsgData->pAuxInfoArray[nIndex].pMediaArray, 0, sizeof(MXD_MEDIA)*nCount );

			for( pSubNode = GetChildElement( pNode, "Media" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "Media" ), nSubIndex++ )
			{
				pEsgData->pAuxInfoArray[nIndex].pMediaArray[nSubIndex].Usage = (MXD_MEDIA_USAGE) GetChildIntValue(pSubNode, "Usage");

				pEsgData->pAuxInfoArray[nIndex].pMediaArray[nSubIndex].Type = (MXD_INT8 *) CloneChildText(pSubNode, "ContentType");

				pEsgData->pAuxInfoArray[nIndex].pMediaArray[nSubIndex].Data = (MXD_INT8 *) CloneChildText(pSubNode, "MediaData");
			}
		}

		nCount = CountChildren( pNode, "ServiceAuxLanguageSpecial" );
		if( nCount )
		{
			MXD_UINT32 nSubIndex;
			nxml_data_t* pSubNode = 0;

			pEsgData->pAuxInfoArray[nIndex].nAuxTextCount = nCount;
			pEsgData->pAuxInfoArray[nIndex].pAuxTextArray = (MXD_SERVICE_AUX_TEXT*) MXD_Malloc( sizeof(MXD_SERVICE_AUX_TEXT)*nCount );
			MXD_Memset( pEsgData->pAuxInfoArray[nIndex].pAuxTextArray, 0, sizeof(MXD_SERVICE_AUX_TEXT)*nCount );

			for( pSubNode = GetChildElement( pNode, "ServiceAuxLanguageSpecial" ), nSubIndex = 0; pSubNode; pSubNode = GetNextElement( pNode, "ServiceAuxLanguageSpecial" ), nSubIndex++ )
			{
				pEsgData->pAuxInfoArray[nIndex].pAuxTextArray[nSubIndex].Lang = (MXD_LANGUAGE_TYPE) GetAttibuteLangValue(pSubNode, "Lang");

				pEsgData->pAuxInfoArray[nIndex].pAuxTextArray[nSubIndex].Description = (MXD_INT8 *) CloneChildText(pSubNode, "ServiceDescription");

				pEsgData->pAuxInfoArray[nIndex].pAuxTextArray[nSubIndex].Provider = (MXD_INT8 *) CloneChildText(pSubNode, "ServiceProvider");

				pEsgData->pAuxInfoArray[nIndex].pAuxTextArray[nSubIndex].LangStr = (MXD_INT8 *) CloneChildText(pSubNode, "ServiceLanguage");
			}
		}
	}
	return TRUE;
}

void RemoveAllAuxInfo( MXD_ESG_DATA *pEsgData )
{
	MXD_UINT16 i,j;
	for( i=0; i<pEsgData->nAuxInfoCount; i++ )
	{
		MXD_Free( pEsgData->pAuxInfoArray[i].ExtentInfo );
		for( j=0; j<pEsgData->pAuxInfoArray[i].nAuxTextCount; j++ )
		{
			MXD_Free( pEsgData->pAuxInfoArray[i].pAuxTextArray[j].Description );
			MXD_Free( pEsgData->pAuxInfoArray[i].pAuxTextArray[j].LangStr );
			MXD_Free( pEsgData->pAuxInfoArray[i].pAuxTextArray[j].Provider );
		}
		MXD_Free( pEsgData->pAuxInfoArray[i].pAuxTextArray );
		for( j=0; j<pEsgData->pAuxInfoArray[i].nMediaCount; j++ )
		{
			MXD_Free( pEsgData->pAuxInfoArray[i].pMediaArray[j].Type );
			MXD_Free( pEsgData->pAuxInfoArray[i].pMediaArray[j].Data );
		}
		MXD_Free( pEsgData->pAuxInfoArray[i].pMediaArray );
	}
	MXD_Free( pEsgData->pAuxInfoArray );
	pEsgData->nAuxInfoCount = 0;
	pEsgData->pAuxInfoArray = 0;
}

void RemoveAllContentInfo( MXD_ESG_DATA *pEsgData )
{
	MXD_UINT16 i,j;
	for( i=0; i<pEsgData->nContentInfoCount; i++ )
	{
		MXD_Free( pEsgData->pContentInfoArray[i].ExtentInfo );
		MXD_Free( pEsgData->pContentInfoArray[i].ContentClass );
		for( j=0; j<pEsgData->pContentInfoArray[i].nTextCount; j++ )
		{
			MXD_Free( pEsgData->pContentInfoArray[i].pTextArray[j].DigestInfo );
			MXD_Free( pEsgData->pContentInfoArray[i].pTextArray[j].Keyword );
			MXD_Free( pEsgData->pContentInfoArray[i].pTextArray[j].Title );
		}
		MXD_Free( pEsgData->pContentInfoArray[i].pTextArray );
		for( j=0; j<pEsgData->pContentInfoArray[i].nMediaCount; j++ )
			MXD_Free( pEsgData->pContentInfoArray[i].pMediaArray[j].Data );
		MXD_Free( pEsgData->pContentInfoArray[i].pMediaArray );
	}
	MXD_Free( pEsgData->pContentInfoArray );
	pEsgData->nContentInfoCount = 0;
	pEsgData->pContentInfoArray = 0;
}

void RemoveAllParaInfo( MXD_ESG_DATA *pEsgData )
{
	MXD_UINT16 i,j;
	for( i=0; i<pEsgData->nParaInfoCount; i++ )
	{
		for( j=0; j<pEsgData->pParaInfoArray[i].nAudioStreamParaCount; j++ )
			MXD_Free( pEsgData->pParaInfoArray[i].pAudioStreamParaArray[j].LATMConfig );
		MXD_Free( pEsgData->pParaInfoArray[i].pAudioStreamParaArray );
		for( j=0; j<pEsgData->pParaInfoArray[i].nVideoStreamParaCount; j++ )
			MXD_Free( pEsgData->pParaInfoArray[i].pVideoStreamParaArray[j].H264ParaSets );
		MXD_Free( pEsgData->pParaInfoArray[i].pVideoStreamParaArray );
	}
	MXD_Free( pEsgData->pParaInfoArray );
	pEsgData->nParaInfoCount = 0;
	pEsgData->pParaInfoArray = 0;
}

void RemoveAllSchInfo( MXD_ESG_DATA *pEsgData )
{
	MXD_UINT16 i,j;
	for( i=0; i<pEsgData->nSchInfoCount; i++ )
	{
		MXD_Free( pEsgData->pSchInfoArray[i].StartTime.Date );
		MXD_Free( pEsgData->pSchInfoArray[i].StartTime.Time );
		for( j=0; j<pEsgData->pSchInfoArray[i].nTitleCount; j++ )
			MXD_Free( pEsgData->pSchInfoArray[i].pTitleArray[j].Str );
		MXD_Free( pEsgData->pSchInfoArray[i].pTitleArray );
	}
	MXD_Free( pEsgData->pSchInfoArray );
	pEsgData->nSchInfoCount = 0;
	pEsgData->pSchInfoArray = 0;
}

void RemoveAllSrvInfo( MXD_ESG_DATA *pEsgData )
{
	MXD_UINT16 i,j;
	for( i=0; i<pEsgData->nSerInfoCount; i++ )
	{
		for( j=0; j<pEsgData->pSerInfoArray[i].nServiceNameCount; j++ )
			MXD_Free( pEsgData->pSerInfoArray[i].pServiceNameArray[j].Name );
		MXD_Free( pEsgData->pSerInfoArray[i].pServiceNameArray );
	}
	MXD_Free( pEsgData->pSerInfoArray );
	pEsgData->nSerInfoCount = 0;
	pEsgData->pSerInfoArray = 0;
}

MXD_UINT32 CreateOrExpandInfoArray( MXD_UINT16 *pOrgCount, void** ppOrgArray, MXD_UINT32 nUnitSize, MXD_UINT16 nCount )
{
	MXD_UINT32 nIndex = 0;
	if( *pOrgCount == 0 )
	{
		*pOrgCount = nCount;
		*ppOrgArray = (MXD_UINT8*) MXD_Malloc( nUnitSize*nCount );
		MXD_Memset( *ppOrgArray, 0, nUnitSize*nCount );
	}
	else
	{
		MXD_UINT32 nNewCount = *pOrgCount + nCount;
		void *pNewInfo = (MXD_UINT8*) MXD_Malloc( nUnitSize*nNewCount );
		MXD_Memset( pNewInfo, 0, nUnitSize*nNewCount );
		MXD_Memcpy( pNewInfo, *ppOrgArray, nUnitSize*(*pOrgCount) );
		if (*ppOrgArray)
			MXD_Free( *ppOrgArray );
		nIndex = *pOrgCount;
		*pOrgCount = nNewCount;
		*ppOrgArray = pNewInfo;
	}
	return nIndex;
}

void sEsgSaver_Init( ESGSAVER* pThis )
{
	MXD_Memset( pThis, 0, sizeof(ESGSAVER) );
}

void sEsgSaver_Destory( ESGSAVER* pThis )
{
	MXD_UINT8 i;
	if( pThis->next != 0 )
		sEsgSaver_Destory( pThis->next );
	if( pThis->m_pEsgBlockBodyLenArray != 0 )
		MXD_Free( pThis->m_pEsgBlockBodyLenArray );
	if( pThis->m_pEsgBlockBodyArray != 0 )
	{
		for( i=0; i<pThis->m_nTotalBlock; i++ )
		{
			if( pThis->m_pEsgBlockBodyArray[i] != 0 )
				MXD_Free( pThis->m_pEsgBlockBodyArray[i] );
		}
		MXD_Free( pThis->m_pEsgBlockBodyArray );
	}
	if( pThis->m_pTotalBuf != 0 )
		MXD_Free( pThis->m_pTotalBuf );
	MXD_Free( pThis );
}

MXD_BOOL sEsgSaver_IsComplete( ESGSAVER* pThis )
{
	MXD_UINT8 i;
	for( i=0; i<pThis->m_nTotalBlock; i++ )
	{
		if( pThis->m_pEsgBlockBodyLenArray[i] == 0 )
			return FALSE;
	}
	return TRUE;
}

MXD_UINT8* sEsgSaver_GetTotalBuffer( ESGSAVER* pThis, MXD_UINT32* pTotalLen )
{
	MXD_UINT8 i;
	MXD_UINT8 *pBuf;
	*pTotalLen = 0;
	if( !sEsgSaver_IsComplete(pThis) )
		return 0;

	if( pThis->m_pTotalBuf )
		MXD_Free( pThis->m_pTotalBuf );

	for( i=0; i<pThis->m_nTotalBlock; i++ )
	{
		*pTotalLen += pThis->m_pEsgBlockBodyLenArray[i];
	}
	pThis->m_pTotalBuf = (MXD_UINT8*) MXD_Malloc( *pTotalLen );

	pBuf = pThis->m_pTotalBuf;
	for( i=0; i<pThis->m_nTotalBlock; i++ )
	{
		MXD_Memcpy( pBuf, pThis->m_pEsgBlockBodyArray[i], pThis->m_pEsgBlockBodyLenArray[i] );
		pBuf += pThis->m_pEsgBlockBodyLenArray[i];
	}
	return pThis->m_pTotalBuf;
}

void sEsgStg_Init( ESGSTG* pThis )
{
	pThis->m_pEsgBlockDataHeader = 0;
	pThis->m_pEsgBlockDataTail = 0;
}

void sEsgStg_Destory( ESGSTG* pThis )
{
	if( pThis->m_pEsgBlockDataHeader )
		sEsgSaver_Destory( pThis->m_pEsgBlockDataHeader );
	pThis->m_pEsgBlockDataHeader = 0;
	pThis->m_pEsgBlockDataTail = 0;
}

ESGSAVER* sEsgStg_GetSavedBlock( ESGSTG* pThis, MXD_UINT8 nBlockId, MXD_UINT8 nBlockTypeId )
{
	struct sEsgBlockSaver* pEsgBlockData = pThis->m_pEsgBlockDataHeader;
	while( pEsgBlockData )
	{
		if( pEsgBlockData->m_nBlockId == nBlockId && pEsgBlockData->m_nBlockTypeId == nBlockTypeId)
			return pEsgBlockData;
		pEsgBlockData = pEsgBlockData->next;
	}
	return 0;
}

MXD_BOOL sEsgStg_AddBlock( ESGSTG* pThis, MXD_UINT8 nBlockId, MXD_UINT8 nBlockTypeId, MXD_UINT8* pMsgData, MXD_UINT32 nBlockLen, MXD_UINT8 nCurrentBlock, MXD_UINT8 nTotalBlock, MXD_BOOL bZipBlock )
{
	ESGSAVER* pEsgBlockData = sEsgStg_GetSavedBlock( pThis, nBlockId, nBlockTypeId );
	if( !pEsgBlockData )
	{
		pEsgBlockData = (ESGSAVER*) MXD_Malloc( sizeof(struct sEsgBlockSaver) );
		sEsgSaver_Init( pEsgBlockData );
		pEsgBlockData->m_nTotalBlock = nTotalBlock;
		pEsgBlockData->m_nBlockId = nBlockId;
		pEsgBlockData->m_nBlockTypeId = nBlockTypeId;
		pEsgBlockData->m_bZipBlock = bZipBlock;
		pEsgBlockData->m_pEsgBlockBodyLenArray = (MXD_UINT32*) MXD_Malloc( sizeof(MXD_UINT32)*nTotalBlock );
		pEsgBlockData->m_pEsgBlockBodyArray = (MXD_UINT8**) MXD_Malloc( sizeof(MXD_UINT8*)*nTotalBlock );
		MXD_Memset( pEsgBlockData->m_pEsgBlockBodyLenArray, 0, sizeof(MXD_UINT32)*nTotalBlock );
		MXD_Memset( pEsgBlockData->m_pEsgBlockBodyArray, 0, sizeof(MXD_UINT8*)*nTotalBlock );
		if( pThis->m_pEsgBlockDataTail )
		{
			pThis->m_pEsgBlockDataTail->next = pEsgBlockData;
			pThis->m_pEsgBlockDataTail = pEsgBlockData;
		}
	}
	if( nCurrentBlock > pEsgBlockData->m_nTotalBlock )
		return FALSE; // todo:
	if( pEsgBlockData->m_pEsgBlockBodyLenArray[ nCurrentBlock ] != 0 )
		return FALSE; //repeat data received!

	pEsgBlockData->m_pEsgBlockBodyLenArray[ nCurrentBlock ] = nBlockLen;
	pEsgBlockData->m_pEsgBlockBodyArray[ nCurrentBlock ] = (MXD_UINT8*) MXD_Malloc( nBlockLen );
	MXD_Memcpy( pEsgBlockData->m_pEsgBlockBodyArray[ nCurrentBlock ], pMsgData, nBlockLen );

	if( ! pThis->m_pEsgBlockDataHeader )
		pThis->m_pEsgBlockDataHeader = pEsgBlockData;
	if( ! pThis->m_pEsgBlockDataTail )
		pThis->m_pEsgBlockDataTail = pEsgBlockData;
	return TRUE;
}

MXD_BOOL sEsgStg_FindCompleteEsgBlock( ESGSTG* pThis, ESGSAVER** ppEsgBlockSaver )
{
	struct sEsgBlockSaver* pEsgBlockData = pThis->m_pEsgBlockDataHeader;
	while( pEsgBlockData )
	{
		if( sEsgSaver_IsComplete(pEsgBlockData) && pEsgBlockData->m_pTotalBuf == 0 ) //only return not used block.
		{
			*ppEsgBlockSaver = pEsgBlockData;
			return TRUE;
		}
		pEsgBlockData = pEsgBlockData->next;
	}
	*ppEsgBlockSaver = 0;
	return FALSE;
}
#endif //USE_EXTERNAL_DEMUXER

void Context_Init( struct sDemuxerContext* pThis )
{
	MXD_Memset( pThis, 0,  sizeof(struct sDemuxerContext) );

	pThis->m_nMediaBufferLen = 12000;
	pThis->m_pMediaBuffer = (MXD_UINT8*) MXD_Malloc( pThis->m_nMediaBufferLen );
	MXD_Memset( pThis->m_pMediaBuffer, 0, pThis->m_nMediaBufferLen );

	sEmgStg_Init( &pThis->m_sEmergencyMsgStorage );
#ifndef USE_EXTERNAL_DEMUXER
	sEsgStg_Init( &pThis->m_sEsgBlockStorage );
#endif //USE_EXTERNAL_DEMUXER

	MXD_Memset( &pThis->m_sEsgDataCtn, 0, sizeof(MXD_ESG_DATA) );
}

void Context_Destory( struct sDemuxerContext* pThis )
{
	if( pThis->m_pSeviceChannelList != 0 )
		MXD_Free( pThis->m_pSeviceChannelList );

	if( pThis->m_pUserParams != 0 )
		MXD_Free( pThis->m_pUserParams );

	if( pThis->m_pEsgDataTypeIDs != 0 )
		MXD_Free( pThis->m_pEsgDataTypeIDs );

	if( pThis->m_pEadtInfoList != 0 )
		MXD_Free( pThis->m_pEadtInfoList );

	MXD_Free( pThis->m_pMediaBuffer );

	sEmgStg_Destory( &pThis->m_sEmergencyMsgStorage );
#ifndef USE_EXTERNAL_DEMUXER
	sEsgStg_Destory( &pThis->m_sEsgBlockStorage );

	RemoveAllAuxInfo( &pThis->m_sEsgDataCtn );
	RemoveAllContentInfo( &pThis->m_sEsgDataCtn );
	RemoveAllParaInfo( &pThis->m_sEsgDataCtn );
	RemoveAllSchInfo( &pThis->m_sEsgDataCtn );
	RemoveAllSrvInfo( &pThis->m_sEsgDataCtn );
#endif //USE_EXTERNAL_DEMUXER

	MXD_Free( pThis );
}

#ifndef USE_EXTERNAL_DEMUXER

void Context_CheckMediaBuffer( struct sDemuxerContext* pThis, MXD_UINT32 nLen )
{
	if( nLen > pThis->m_nMediaBufferLen-10 )
	{
		MXD_Free( pThis->m_pMediaBuffer );
		pThis->m_nMediaBufferLen = nLen+200;
		pThis->m_pMediaBuffer = (MXD_UINT8*) MXD_Malloc( pThis->m_nMediaBufferLen );
		MXD_Memset( pThis->m_pMediaBuffer, 0, pThis->m_nMediaBufferLen );
	}
}

void Context_FillAacFrame( struct sDemuxerContext* pThis, MXD_UINT8* pSrcFrame, MXD_UINT32 nFrameLen, MXD_UINT32 nBufferBegin )
{
	Context_CheckMediaBuffer( pThis, nFrameLen );
	nFrameLen += 7;
	pThis->m_pMediaBuffer[nBufferBegin+0] = 0xFF;
	pThis->m_pMediaBuffer[nBufferBegin+1] = 0xF9;
	pThis->m_pMediaBuffer[nBufferBegin+2] = 0x58;
	pThis->m_pMediaBuffer[nBufferBegin+3] = 0x80 | ( 0x03 & (nFrameLen >> 11) );
	pThis->m_pMediaBuffer[nBufferBegin+4] = (nFrameLen >> 3) & 0xFF;
	pThis->m_pMediaBuffer[nBufferBegin+5] = 0x1F | ( (nFrameLen & 0x7)<<5 );
	pThis->m_pMediaBuffer[nBufferBegin+6] = 0xFC;
	MXD_Memcpy( pThis->m_pMediaBuffer + nBufferBegin + 7, pSrcFrame, nFrameLen );
}

#endif //USE_EXTERNAL_DEMUXER

MXD_BOOL Context_GetSrvIdAndUserParam( struct sDemuxerContext* pThis, MXD_UINT8 nMF_ID, MXD_UINT32* pServiceID, void** ppSeviceUserParam )
{
	MXD_UINT32 j;
	*pServiceID = 0;
	*ppSeviceUserParam = 0;
	for( j=0; j<pThis->m_nServiceCount; j++ )
	{
		if( pThis->m_pSeviceChannelList[j].nMF_ID == nMF_ID )
		{
			*pServiceID = pThis->m_pSeviceChannelList[j].nServiceID;
			*ppSeviceUserParam = pThis->m_pUserParams[j];
			return TRUE;
		}
	}
	return FALSE;
}

MXD_BOOL Context_GetMFID( struct sDemuxerContext* pThis, MXD_UINT32 nServiceID, MXD_UINT8 *pMF_ID, MXD_UINT8 *pSubFrameId )
{
	MXD_UINT32 j;
	*pMF_ID = 0;
	for( j=0; j<pThis->m_nServiceCount; j++ )
	{
		if( pThis->m_pSeviceChannelList[j].nServiceID == nServiceID )
		{
			*pMF_ID = pThis->m_pSeviceChannelList[j].nMF_ID;
			*pSubFrameId = pThis->m_pSeviceChannelList[j].nSubFrameID;
			return TRUE;
		}
	}
	return FALSE;
}

MXD_BOOL Context_GetUserParam( struct sDemuxerContext* pThis, MXD_UINT32 nServiceID, void* *ppSeviceUserParam )
{
	MXD_UINT32 j;
	*ppSeviceUserParam = 0;
	for( j=0; j<pThis->m_nServiceCount; j++ )
	{
		if( pThis->m_pSeviceChannelList[j].nServiceID == nServiceID )
		{
			*ppSeviceUserParam = pThis->m_pUserParams[j];
			return TRUE;
		}
	}
	return FALSE;
}

void PrintCmmbUnit( CMMBUNIT_DEFINE* pUnit )
{
	while( pUnit && pUnit->m_szNodeName )
	{
		if( !pUnit->m_bActive  )
			MXD_DebugPrint( "<<<%s>>>----Disabled!\n", pUnit->m_szNodeName );
		else if( pUnit->m_u1.m_pChildUnitList )
		{
			CMMBUNIT_DEFINE* pSubNode = pUnit->m_u1.m_pChildUnitList;
			MXD_DebugPrint( "(((%s)))******Child Begin****************************************%x\n", pUnit->m_szNodeName, pUnit );
			PrintCmmbUnit( pSubNode );
			MXD_DebugPrint( "(((%s)))$$$$$$Child End$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n", pUnit->m_szNodeName );
		}
		else if( pUnit->m_bActive  )
			MXD_DebugPrint( ":%s\t\tValue:0x%x \tBits=%d,Content=0x%x\n", pUnit->m_szNodeName, pUnit->m_nValue, (int)(pUnit->m_nContentBitLen), ((pUnit->m_pContent) ? ((unsigned char)*pUnit->m_pContent) : 0xffff) );

		if( !(pUnit+1)->m_szNodeName && (pUnit+1)->m_u2.m_pNextUnitList )
		{
			pUnit = (pUnit+1)->m_u2.m_pNextUnitList;
			MXD_DebugPrint( "==============================>Into Next List--%x\n", pUnit );
		}
		else
			pUnit++;
	}
}
